Question Quand utiliser "volatile" ou "Thread.MemoryBarrier ()" dans le code de verrouillage threadsafe? (C #)


Quand dois-je utiliser volatile / Thread.MemoryBarrier () pour la sécurité des threads?


16
2017-08-25 19:58


origine


Réponses:


Tu utilises volatile/Thread.MemoryBarrier() lorsque vous souhaitez accéder à une variable entre threads sans verrouiller.

Des variables atomiques, comme un int par exemple, sont toujours lus et écrits en même temps. Cela signifie que vous n'obtiendrez jamais la moitié de la valeur avant qu'un autre thread le modifie et l'autre moitié après avoir changé. À cause de cela, vous pouvez lire et écrire la valeur en toute sécurité dans différents threads sans synchroniser.

Cependant, le compilateur peut optimiser certaines lectures et écritures, ce que vous empêchez avec le volatile mot-clé. Si vous avez par exemple une boucle comme ceci:

sum = 0;
foreach (int value in list) {
   sum += value;
}

Le compilateur peut en fait effectuer les calculs dans un registre de processeur et écrire uniquement la valeur dans le registre. sum variable après la boucle. Si vous faites le sum variable volatile, le compilateur générera du code qui lit et écrit la variable pour chaque modification, afin que sa valeur soit à jour tout au long de la boucle.


20
2017-08-25 20:21



Quel est le problème avec

private static readonly object syncObj = new object();
private static int counter;

public static int NextValue()
{
    lock (syncObj)
    {
        return counter++;
    }
}

?

Cela fait tout le verrouillage nécessaire, les barrières mémoire, etc. pour vous. Il est bien compris et plus lisible que tout code de synchronisation personnalisé basé sur volatile et Thread.MemoryBarrier().


MODIFIER

Je ne peux pas penser à un scénario dans lequel j'utiliserais volatile ou Thread.MemoryBarrier(). Par exemple

private static volatile int counter;

public static int NextValue()
{
    return counter++;
}

est ne pas équivalent au code ci-dessus et est ne pas thread-safe (volatile ne fait pas ++ devenez comme par magie sans fil).

Dans un cas comme celui-ci:

private static volatile bool done;

void Thread1()
{
    while (!done)
    {
        // do work
    }
}

void Thread2()
{
    // do work
    done = true;
}

(qui devrait fonctionner) J'utiliserais un ManualResetEvent pour signaler que Thread2 est terminé.


10
2017-08-25 20:02



Fondamentalement, si vous utilisez un autre type de synchronisation pour rendre votre code threadsafe, vous n'avez pas besoin de le faire.

La plupart des mécanismes de verrouillage (y compris le verrouillage) impliquent automatiquement une barrière de mémoire afin que plusieurs processeurs puissent obtenir les informations correctes.

Volatile et MemoryBarrier sont principalement utilisés dans des scénarios sans verrou où vous essayez d’éviter la perte de performance liée au verrouillage.

Modifier: Vous devriez lire Cet article par Joe Duffy sur le modèle de mémoire CLR 2.0, cela clarifie beaucoup de choses (si vous êtes vraiment intéressé, vous devriez lire TOUS les articles de Joe Duffie, qui est le plus expert en parallélisme dans .NET)


10
2017-08-25 20:02



Comme le nom implique des garanties volatiles, la valeur du cache est vidée en mémoire pour que tous les threads voient la même valeur. Par exemple, si j'ai un entier dont la dernière écriture est enregistrée dans le cache, les autres threads risquent de ne pas voir cela. Ils peuvent même voir leur copie cache de cet entier. Marquer une variable comme volatile permet de la lire directement dans la mémoire.

Sriwantha Sri Aravinda Attanayake


0
2017-09-04 14:34