N'est-ce pas atomic<bool>
_ redondant parce que bool
est atomique par nature? Je ne pense pas qu'il soit possible d'avoir une valeur bool partiellement modifiée. Quand ai-je vraiment besoin d'utiliser atomic<bool>
au lieu de bool
?
No le type en C++ est "de nature atomique" sauf s'il s'agit d'un std::atomic*
-quelque chose. C'est parce que la norme le dit.
En pratique, les instructions matérielles réelles émises pour manipuler un std::atomic<bool>
peut être (ou ne peut pas être) identique à celui d’un bool
ordinaire, mais être atomique est un concept plus vaste aux ramifications plus larges (par exemple, des restrictions sur le réordonnancement du compilateur). De plus, certaines opérations (comme la négation) sont surchargées sur l'opération atomique pour créer une instruction matériellement distincte de la séquence native, non atomique de lecture-modification-écriture d'une variable non atomique.
Rappelez-vous à propos de barrières de mémoire . Bien qu'il puisse être impossible de modifier partiellement bool
, il est possible que le système multiprocesseur ait cette variable dans plusieurs copies et qu'un thread puisse voir l'ancienne valeur même après qu'un autre thread l'a modifiée. Atomic introduit une barrière de mémoire, il devient donc impossible.
Les types atomiques de C++ traitent trois problèmes potentiels. Tout d'abord, une lecture ou une écriture peut être déchirée par un commutateur de tâche si l'opération nécessite plusieurs opérations de bus (et que peut arriver à un bool
, selon la manière dont elle est mise en œuvre). Deuxièmement, une lecture ou une écriture peut n'affecter que le cache associé au processeur qui effectue l'opération, et d'autres processeurs peuvent avoir une valeur différente dans leur cache. Troisièmement, le compilateur peut réorganiser l'ordre des opérations si elles n'affectent pas le résultat (les contraintes sont un peu plus compliquées, mais c'est suffisant pour le moment).
Vous pouvez gérer vous-même chacun de ces trois problèmes en faisant des suppositions sur la manière dont les types que vous utilisez sont implémentés, en vidant explicitement les caches et en utilisant des options spécifiques au compilateur pour empêcher la réorganisation (et non, volatile
ne le fait pas sauf si la documentation du compilateur le dit).
Mais pourquoi traverser tout ça? atomic
s'en occupe pour vous et fait probablement un meilleur travail que vous ne pouvez le faire vous-même.
Les opérations atomiques ne se limitent pas à des valeurs déchirées. Par conséquent, bien que je sois d’accord avec vous et d’autres affiches, je ne suis pas au courant d’un environnement dans lequel la possibilité de déchirer bool
est une possibilité, mais l’enjeu est plus important.
Herb Sutter a donné une excellente conférence à ce sujet, que vous pouvez consulter en ligne. Soyez averti, c'est une conversation longue et compliquée. Herb Sutter, Atomic Weapons . Le problème consiste essentiellement à éviter les courses de données, car il vous donne l’illusion d’une cohérence séquentielle.
Considérons une opération de comparaison et d'échange:
bool a = ...;
bool b = ...;
if (a)
swap(a,b);
Après avoir lu a, nous obtenons un vrai, un autre thread pourrait arriver et définir un faux, nous échangerons (a, b), donc après la sortie b sera faux, même si l'échange a été effectué.
En utilisant std::atomic::compare_exchange
nous pouvons faire la totalité de la logique if/swap atomiquement de sorte que l’autre thread ne puisse pas définir la valeur false sur false entre if et le swap (sans verrouillage). Dans un tel cas, si l’échange a été effectué, b doit être faux à la sortie.
Ce n'est qu'un exemple d'une opération atomique qui s'applique à un type à deux valeurs tel que bool.
L'atomicité de certains types dépend exclusivement du matériel sous-jacent. Chaque architecture de processeur a différentes garanties sur l'atomicité de certaines opérations. Par exemple:
Le processeur Intel486 (et les processeurs plus récents depuis) garantit que les opérations de mémoire de base suivantes seront toujours effectuées de manière atomique:
- Lecture ou écriture d'un octet
- Lecture ou écriture d'un mot aligné sur une limite de 16 bits
- Lecture ou écriture d'un mot double aligné sur une limite de 32 bits
D'autres architectures ont des spécifications différentes sur lesquelles les opérations sont atomiques.
C++ est un langage de programmation de haut niveau qui s'efforce de vous séparer du matériel sous-jacent. Pour cette raison, la norme ne peut tout simplement pas permettre de s’appuyer sur de telles hypothèses de bas niveau, sans quoi votre application ne serait pas portable. En conséquence, tous les types primitifs en C++ sont fournis avec des équivalents atomic
par une bibliothèque standard conforme à C++ 11 prête à l'emploi.