31 votes

Le mot clé 'volatile' est-il toujours cassé en C #?

Joe Albahari a une grande série sur le multithreading, c'est une lecture indispensable et doit être connu par cœur pour tous ceux qui font de C# multithreading.

Dans la partie 4 toutefois, il mentionne les problèmes avec les volatiles:

Notez que l'application de volatiles qui n'empêche pas une écriture suivie par un lire d'être échangé, et cela peut créer des casse-têtes. Joe Duffy illustre le problème avec l'exemple suivant: si Test1 et Test2 exécuter simultanément sur les différents threads, il est possible pour un et b à la fois jusqu'à la fin avec une valeur de 0 (malgré l'utilisation de la volatilité sur à la fois x et y)

Suivie par une note que la documentation MSDN est incorrect:

La documentation MSDN stipule que l'utilisation du mot clé volatile assure que le les plus up-to-date de valeur est présente dans le champ à tout moment. C'est inexact, puisque, comme nous l'avons vu, une écriture suivie par une lecture peut être réorganisées.

J'ai vérifié la documentation MSDN, qui a été modifié en 2015, mais encore les listes:

Le mot clé volatile indique qu'un champ peut être modifié par plusieurs threads qui s'exécutent en même temps. Les champs qui sont déclaré volatiles ne sont pas soumis à des optimisations du compilateur que supposons accès par un seul thread. Cela garantit que la plupart des up-to-date de valeur est présente dans le champ à tout moment.

Droit maintenant, j'ai toujours éviter de volatiles en faveur de la plus prolixe pour éviter de threads à l'aide des données périmées:

private int foo;
private object fooLock = new object();
public int Foo {
    get { lock(fooLock) return foo; }
    set { lock(fooLock) foo = value; }
}

Comme les parties à propos de multithreading ont été écrites en 2011, l'argument est-il encore valable aujourd'hui? Devrait volatils encore être évité à tout prix en faveur de serrures ou de mémoire complète de clôtures pour empêcher l'introduction très dur pour produire des bugs, comme l'a mentionné sont même dépend de la CPU fournisseur, il est en cours d'exécution sur?

32voto

Voo Points 11981

Volatile dans son implémentation n'est pas cassé malgré populaire blogue affirmant une telle chose. Cependant, il est mal spécifié et l'idée d'utiliser un modificateur sur un champ pour spécifier la mémoire de la commande n'est pas très bonne (comparer les volatiles en Java/C# C++'s atomique spécification eu assez de temps pour apprendre à partir des erreurs précédentes). L'article MSDN sur l'autre main a clairement été écrit par quelqu'un qui n'a pas à parler de simultanéité et est complètement faux.. le seul sain d'esprit option est complètement l'ignorer.

Volatilité des garanties d'acquisition/diffusion de la sémantique lors de l'accès au terrain et ne peut être appliqué à des types qui permettent atomique, les lectures et les écritures. Pas plus, pas moins. C'est assez pour être utile pour mettre en œuvre de nombreux algorithmes sans verrouillage efficace comme non-bloquant hashmaps.

Un très simple échantillon à l'aide d'une variable volatile pour publier des données. Grâce à la volatilité sur x, l'affirmation dans l'extrait de code suivant ne peut pas le feu:

private int a;
private volatile bool x;

public void Publish()
{
    a = 1;
    x = true;
}

public void Read()
{
    if (x)
    {
        // if we observe x == true, we will always see the preceding write to a
        Debug.Assert(a == 1); 
    }
}

Volatile n'est pas facile à utiliser et, dans la plupart des situations, vous êtes beaucoup mieux d'aller avec un niveau supérieur concept, mais quand la performance est importante ou que vous soyez à la mise en œuvre de certains aspects bas niveau des structures de données, la volatilité peut être extrêmement utile.

13voto

Charles Bretana Points 59899

En lisant la documentation MSDN, je pense que cela signifie que si vous voyez une variable volatile, vous n'avez pas à vous soucier de l'optimisation du compilateur, qui fausse la valeur car elles réorganisent les opérations. Cela ne signifie pas que vous êtes protégé contre les erreurs causées par votre propre code exécutant des opérations sur des threads séparés dans le mauvais ordre. (Bien que certes, le commentaire n'est pas clair à ce sujet.)

2voto

zstewart Points 1564

volatile est très limitée garantie. Cela signifie que la variable n'est pas soumis à des optimisations du compilateur qui supposent l'accès à partir d'un seul fil. Cela signifie que si vous écrivez dans une variable à partir d'un fil, puis le lire à partir d'un autre thread, l'autre thread aura certainement la dernière valeur. Sans volatile, une machine multiprocesseur sans volatiles, le compilateur peut faire des hypothèses sur un seul thread d'accès, par exemple en gardant la valeur dans un registre, ce qui empêche les autres processeurs d'avoir accès à la dernière valeur.

Comme l'exemple de code que vous avez mentionné, il ne vous protège pas d'avoir des méthodes dans les différents blocs réorganisées. En effet volatile fait de chaque individu d'accéder à un volatile variable atomique. Il ne fait aucune garantie quant à l'atomicité des groupes de ces accès.

Si vous voulez juste pour s'assurer que votre propriété a une date de valeur unique, vous devriez être en mesure de l'utiliser juste volatile.

Le problème vient de si vous essayez d'effectuer plusieurs opérations en parallèle, comme s'ils étaient atomique. Si vous devez forcer plusieurs opérations atomiques ensemble, vous avez besoin de verrouiller l'ensemble de l'opération. Prenons l'exemple de nouveau, mais l'utilisation de verrous:

class DoLocksReallySaveYouHere
{
  int x, y;
  object xlock = new object(), ylock = new object();

  void Test1()        // Executed on one thread
  {
    lock(xlock) {x = 1;}
    lock(ylock) {int a = y;}
    ...
  }

  void Test2()        // Executed on another thread
  {
    lock(ylock) {y = 1;}
    lock(xlock) {int b = x;}
    ...
  }
}

Les serrures cause peut causer de la synchronisation, ce qui peut empêcher les deux a et b de la valeur à 0 (je n'ai pas testé). Cependant, puisque les deux x et y sont enfermés de façon indépendante, soit en a ou b peut encore non-déterministe jusqu'à la fin avec une valeur de 0.

Ainsi, dans le cas d'emballage de la modification d'une seule variable, vous devriez être en sécurité à l'aide de volatile, et ne serait pas vraiment plus de sécurité à l'aide d' lock. Si vous avez besoin de atomiquement effectuer de multiples opérations, vous devez utiliser un lock autour de l'ensemble de la atomique en bloc, sinon programmation sera toujours provoquer un comportement non déterministe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X