93 votes

Les lectures et écritures d'un int en C++ sont-elles atomiques ?

J'ai deux fils, un qui met à jour un int et un qui le lit. Il s'agit d'une valeur statistique pour laquelle l'ordre des lectures et des écritures n'a aucune importance.

Ma question est la suivante : dois-je de toute façon synchroniser l'accès à cette valeur multi-octet ? Ou, en d'autres termes, une partie de l'écriture peut-elle être terminée et interrompue, puis la lecture peut avoir lieu.

Par exemple, pensez à une valeur = 0x0000FFFF qui reçoit une valeur incrémentée de 0x00010000.

Y a-t-il un moment où la valeur ressemble à 0x0001FFFF dont je devrais m'inquiéter ? Il est certain que plus le type est grand, plus il est possible que quelque chose comme ça se produise.

J'ai toujours synchronisé ces types d'accès, mais je suis curieux de savoir ce que pense la communauté.

6 votes

Vraiment ? Je ne me soucierais pas de ce que la communauté pense. Ce qui m'intéresse, ce sont les faits :)

1 votes

Une lecture intéressante sur le sujet : channel9.msdn.com/Shows/Going+Deep/

0 votes

En particulier pour = : stackoverflow.com/questions/8290768/

65voto

Skizz Points 30682

Quelle question ! La réponse est :

Oui, non, hmmm, eh bien, ça dépend.

Tout dépend de l'architecture du système. Sur un IA32, une adresse correctement alignée sera une opération atomique. Les écritures non alignées peuvent être atomiques, cela dépend du système de cache utilisé. Si la mémoire se trouve dans une seule ligne de cache L1, l'opération est atomique, sinon elle ne l'est pas. La largeur du bus entre le CPU et la RAM peut affecter la nature atomique : une écriture 16 bits correctement alignée sur un 8086 était atomique alors que la même écriture sur un 8088 ne l'était pas car le 8088 n'avait qu'un bus 8 bits alors que le 8086 avait un bus 16 bits.

En outre, si vous utilisez C/C++, n'oubliez pas de marquer la valeur partagée comme volatile, sinon l'optimiseur pensera que la variable n'est jamais mise à jour dans l'un de vos threads.

25 votes

Le mot-clé volatile n'est pas utile dans les programmes multithreads. stackoverflow.com/questions/2484980/

6 votes

@IngeHenriksen : Je ne suis pas convaincu par ce lien.

0 votes

Une autre source, mais malheureusement très ancienne (elle est antérieure à std::atomic) : web.archive.org/web/20190219170904/https://software.intel.com/

48voto

Adam Mitz Points 4540

Au début, on pourrait penser que les lectures et les écritures de la taille native de la machine sont atomiques, mais il y a un certain nombre de problèmes à gérer, notamment la cohérence du cache entre les processeurs/cores. Utilisez des opérations atomiques comme Interlocked* sous Windows et l'équivalent sous Linux. C++0x disposera d'un modèle "atomique" pour envelopper ces opérations dans une interface agréable et multiplateforme. Pour l'instant, si vous utilisez une couche d'abstraction de plate-forme, elle peut fournir ces fonctions. ACE le fait, voir le modèle de classe ACE_Atomic_Op .

0 votes

Le document de ACE_Atomic_Op a été déplacé - il se trouve désormais à l'adresse suivante dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ace/Atomic_Op.inl

11voto

gabr Points 20458

Si vous lisez/écrivez une valeur de 4 octets ET qu'elle est alignée sur un DWORD dans la mémoire ET que vous utilisez l'architecture I32, alors les lectures et les écritures sont atomiques.

3 votes

Où cela est-il indiqué dans les manuels du développeur de logiciels de l'architecture Intel ?

2 votes

@DanielTrebbien : peut-être voir stackoverflow.com/questions/5002046/

10voto

Anthony Williams Points 28904

Oui, vous devez synchroniser les accès. En C++0x, il s'agira d'une course aux données et d'un comportement indéfini. Avec les threads POSIX, c'est déjà un comportement indéfini.

En pratique, vous pouvez obtenir de mauvaises valeurs si le type de données est plus grand que la taille native du mot. De plus, un autre thread peut ne jamais voir la valeur écrite en raison d'optimisations déplaçant la lecture et/ou l'écriture.

3voto

Jason Cohen Points 36475

Vous devez synchroniser, mais sur certaines architectures, il existe des moyens efficaces de le faire.

Le mieux est d'utiliser des sous-routines (peut-être masquées derrière des macros) afin de pouvoir remplacer conditionnellement les implémentations par des implémentations spécifiques à la plate-forme.

Le noyau Linux possède déjà une partie de ce code.

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