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 ».

Était-ce utile?

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 boucle Thread.MemoryBarrier while corrigera le problème. Ou vous pouvez même utiliser Volatile.Read [ou un champ volatile]. 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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top