Interlocked ne fournit la visibilité dans toutes les discussions?
-
21-08-2019 - |
Question
Supposons que j'ai une variable « compteur », et il y a plusieurs threads accédant et en définissant la valeur de « compteur » à l'aide de Contrefil, i.e.:.
int value = Interlocked.Increment(ref counter);
et
int value = Interlocked.Decrement(ref counter);
Puis-je suppose que, la modification apportée par Interlocked sera visible dans toutes les discussions?
Dans le cas contraire, que dois-je faire pour toutes les discussions synchronisent la variable?
EDIT: quelqu'un m'a suggéré d'utiliser volatile. Mais quand je mets le « contre » comme volatile, il y a avertissement du compilateur « référence au champ volatile ne sera pas traitée comme volatile ».
Quand je lis l'aide en ligne, il a dit, « ne devrait pas normalement être passé un champ volatile à l'aide d'un ref ou hors paramètre ».
La solution
InterlockedIncrement / décrémentation sur les processeurs x86 (x86 de verrouillage ajouter / dec) créent automatiquement barrière de mémoire qui donne la visibilité à tous les threads (toutes les discussions peuvent voir sa mise à jour comme dans l'ordre, comme séquentiel la cohérence de la mémoire). barrière de mémoire rend toutes les charges de mémoire en attente / magasins à compléter. N'est pas liée volatile
à cette question, bien que C # et Java (et certains compilateurs C / C ++) pour faire appliquer la barrière <=> mémoire. Mais, l'opération a déjà agrafé barrière de mémoire par CPU.
S'il vous plaît aussi jeter un oeil mon autre réponse en stackoverflow.
Notez que je suppose que InterlockedIncrement / décrémentation C # Les 's sont intrinsèques à la cartographie x86 de verrouillage ajouter / dec.
Autres conseils
Puis-je suppose que, la modification apportée par Interlocked sera visible dans toutes les discussions?
Cela dépend de la façon dont vous lisez la valeur. Si vous « juste » lire, alors non, ce ne sera pas toujours visible dans d'autres sujets sauf si vous définissez comme volatile. Cela provoque un avertissement gênant cependant.
Comme alternative (et l'OMI beaucoup préféré), lu à l'aide d'une autre instruction Interlocked. Ce sera toujours voir la valeur actualisée sur tous les sujets:
int readvalue = Interlocked.CompareExchange(ref counter, 0, 0);
qui retourne la valeur lue, et si elle était 0 avec 0 swaps il.
Motivation: l'avertissement laisse entendre que quelque chose ne va pas; combinant les deux techniques (volatiles et interverrouillage) n'a pas été de la manière prévue pour le faire.
Mise à jour: il semble qu'une autre approche de 32 bits fiable lit sans utiliser « volatile » est en utilisant comme suggéré dans Thread.VolatileRead
cette réponse . Il y a aussi des preuves que je suis tout à fait tort sur l'utilisation pour les lectures 32 Interlocked
bits, par exemple Connect problème , bien que je me demande si la distinction est un peu pédant dans la nature.
Ce que je veux dire vraiment est: ne pas utiliser cette réponse comme seule source; Je vais avoir mes doutes à ce sujet.
En fait, ils ne sont pas. Si vous souhaitez modifier en toute sécurité counter
, alors vous faites la bonne chose. Mais si vous voulez lire directement vous devez volatile
déclarer comme Interlocked
. Dans le cas contraire, le compilateur n'a aucune raison de croire que va changer parce que <=> les opérations sont dans le code <=> qu'il pourrait ne pas voir.
Interlocked assure que seulement 1 fil à la fois peut mettre à jour la valeur. Pour faire en sorte que d'autres threads peuvent lire la valeur correcte (et non une valeur en cache) marquer comme volatile.
int volatile publique contre;
Non; verrouillables-à-écriture seule seule ne pas assurer cette variable se lit dans le code sont en fait frais; un programme qui ne lit pas correctement à partir d'un champ aussi bien pourrait ne pas être thread-safe , même sous un « modèle de mémoire forte ». Ceci est valable pour toutes les formes d'assigner à un domaine partagé entre les threads.
Voici un exemple de code qui ne sera jamais fin en raison du JIT . (Il a été modifié à partir de Barrières de mémoire dans. NET pour un programme de mise à jour pour LINQPad runnable la question).
// Run this as a LINQPad program in "Release Mode".
// ~ It will never terminate on .NET 4.5.2 / x64. ~
// The program will terminate in "Debug Mode" and may terminate
// in other CLR runtimes and architecture targets.
class X {
// Adding {volatile} would 'fix the problem', as it prevents the JIT
// optimization that results in the non-terminating code.
public int terminate = 0;
public int y;
public void Run() {
var r = new ManualResetEvent(false);
var t = new Thread(() => {
int x = 0;
r.Set();
// Using Volatile.Read or otherwise establishing
// an Acquire Barrier would disable the 'bad' optimization.
while(terminate == 0){x = x * 2;}
y = x;
});
t.Start();
r.WaitOne();
Interlocked.Increment(ref terminate);
t.Join();
Console.WriteLine("Done: " + y);
}
}
void Main()
{
new X().Run();
}
L'explication de Barrières de mémoire dans .NET :
Cette fois-ci, il est JIT, pas le matériel. Il est clair que JIT a mis en cache la valeur de la variable fin [dans le registre EAX et le] programme est maintenant bloqué dans la boucle en surbrillance ci-dessus. .
Soit en utilisant un ou l'ajout d'un
lock
intérieur de la boucleThread.MemoryBarrier
while corrigera le problème. Ou vous pouvez même utiliserVolatile.Read
[ou un champvolatile
]. Le but de la barrière de mémoire est ici que pour supprimer les optimisations JIT. Maintenant que nous avons vu comment logiciels et le matériel peut réorganiser les opérations de mémoire , il est temps de discuter des obstacles de mémoire ..
est, une somme supplémentaire barrière construction est nécessaire du côté de la lecture à éviter des problèmes avec la compilation et JIT réordonnancement / optimisations : c'est une question différente de celle de la mémoire cohérence!
Ajout ici serait <=> empêcher l'optimisation JIT, et donc « résoudre le problème », même si ces résultats dans un avertissement. Ce programme peut également être corrigé par l'utilisation de <=> ou l'un des autres opérations diverses qui provoquent une barrière:. Ces obstacles sont autant une partie de la correction du programme CLR / JIT comme les clôtures de mémoire matérielle sous-jacente