C ++ 11 Ordre de la mémoire atomique avec variables non atomiques

Je ne suis pas sûr de savoir comment les garanties de classement de la mémoire des variables atomiques dans c ++ 11 affectent les opérations avec une autre mémoire.

Disons que j’ai un thread qui appelle périodiquement la fonction write pour mettre à jour une valeur et un autre thread qui appelle read pour obtenir la valeur actuelle. Est-il garanti que les effets de d = value; ne sera pas vu avant les effets d’ a = version; , et sera vu avant les effets de b = version; ?

 atomic a {0}; atomic b {0}; double d; void write(int version, double value) { a = version; d = value; b = version; } double read() { int x,y; double ret; do { x = b; ret = d; y = a; } while (x != y); return ret; } 

Est-il garanti que les effets de d = value; ne sera pas vu avant les effets d’ a = version; , et sera vu avant les effets de b = version; ?

Oui, ça l’est. En effet, une barrière de cohérence séquentielle est impliquée lors de la lecture ou de l’écriture d’une variable atomic<> .

Au lieu de stocker la balise de version dans deux variables atomiques avant la modification de la valeur et après, vous pouvez incrémenter une variable atomique unique avant et après la modification:

 atomic a = {0}; double d; void write(double value) { a = a + 1; // 'a' become odd d = value; //or other modification of protected value(s) a = a + 1; // 'a' become even, but not equal to the one before modification } double read(void) { int x; double ret; do { x = a; ret = value; // or other action with protected value(s) } while((x & 2) || (x != a)); return ret; } 

Ceci est appelé seqlock dans le kernel Linux: http://en.wikipedia.org/wiki/Seqlock

La règle est que, étant donné qu’un thread d’ write s’exécute une fois, et rien d’autre qui modifie a , b ou d ,

  • Vous pouvez lire a et b depuis un fil différent à tout moment, et
  • si vous lisez b et voyez la version stockée, alors
    • Vous pouvez lire d ; et
    • Ce que vous lisez sera une value .

Notez que si la deuxième partie est vraie dépend du classement de la mémoire; c’est vrai avec le défaut ( memory_order_seq_cst ).

Votre object d est écrit et lu par deux threads et ce n’est pas atomique. Ceci est dangereux, comme suggéré dans la norme C ++ sur le multithreading:

1.10 / 4 Deux évaluations d’expression sont en conflit si l’une d’elles modifie un emplacement de mémoire et que l’autre accède ou modifie le même emplacement de mémoire.

1.10 / 21 L’exécution d’un programme contient une course de données si elle contient deux actions en conflit dans des threads différents, dont au moins une n’est pas atomique et ne se produit pas avant l’autre. Une telle course de données entraîne un comportement indéfini.

Modification importante:

Dans votre cas non atomique, vous n’avez aucune garantie quant à l’ordre entre la lecture et l’écriture. Vous n’avez même pas la garantie que le lecteur lira une valeur écrite par l’écrivain (ce court article explique le risque associé aux variables non atomiques).

Néanmoins , la boucle de votre lecteur se termine sur la base d’un test des variables atomiques environnantes, pour lesquelles il existe de fortes garanties. En supposant que cette version ne répète jamais entre différents appels d’écrivains, et compte tenu de l’ordre inverse dans lequel vous acquérez leur valeur:

  • l’ordre du d lu par rapport à l’écriture d ne peut pas être regrettable si les deux atomes sont égaux.
  • de même, la valeur lue ne peut pas être incohérente si les deux atomes sont égaux.

Cela signifie qu’en cas de condition de concurrence défavorable sur votre non-atomique, grâce à la boucle, vous lirez la dernière value .