2017-09-21 5 views
0

Parfois, vous pouvez vouloir accéder à une variable à la fois atomiquement et non atomiquement. Quelle est la raison pour laquelle je trouve convinient que sur gcc vous pouvez écrire quelque chose comme:C11 mélange accès atomique et non-atomique à la variable

int var = 0; 
var++; 
atomic_fetch_add(&var, 1); 

Toutefois, cela ne compile pas avec clang 4.0.1:

error: address argument to atomic operation must be a pointer to _Atomic type ('int *' invalid) 
atomic_fetch_add(&var, 1); 

La meilleure solution que je pourrais trouver est un casting:

int var = 0; 
(*(int*)&var)++; 
atomic_fetch_add(&var, 1); 

Existe-t-il un moyen plus simple et portable d'y parvenir?

+0

"ceci ne compile pas avec clang" --- un message d'erreur serait utile. – zerkms

+0

Btw, quel est le but initial de faire cela? Le premier changement de valeur n'est pas garanti pour être vu par un autre thread, donc la valeur 'var ++;' peut être effectivement pas du tout appliquée. – zerkms

+1

Comment * cela * pourrait-il fonctionner? '_Atomic int' et' int' sont des types différents, donc les pointeurs ne seront pas convertis automatiquement. Si vous lancez manuellement, vous obtenez un comportement indéfini pour l'accès à un objet via une expression lvalue qui ne correspond pas au type effectif de l'objet (et non à un type de caractère). – EOF

Répondre

0

La machine abstraite définie par le standard C a une vue de stockage plutôt différente de la plupart des machines réelles. En particulier, plutôt que de considérer les accès à la mémoire comme des actions qui peuvent être effectuées de différentes manières en fonction des circonstances requises, chaque objet a un type de lecture et au maximum un type d'écriture (const). ne supporte aucun type d'écriture); le type de lecture et d'écriture requis pour accéder à un objet dépend de son type.

Une telle approche peut être utile pour certains types de plates-formes matérielles, ou pour certaines stratégies d'optimisation, mais est tout à fait inadaptée pour de nombreux types de programmes s'exécutant sur des plates-formes réelles. Malheureusement, la norme ne reconnaît aucun moyen pratique par lequel les programmeurs peuvent indiquer que certains objets doivent être traités comme un stockage «ordinaire» la plupart du temps, mais reconnaissent qu'ils nécessitent des sémantiques de mémoire plus précises à certains moments spécifiques pendant l'exécution du programme.

2

Il existe deux interfaces dans C11 qui vous permettent d'agir sur un objet atomique moins contraignant. Tout d'abord, vous pouvez toujours remplacer un objet atomique, sachant que vous êtes le seul à y accéder, généralement lors d'une phase d'initialisation, utilisez atomic_init pour cela. Deuxièmement, si vous avez besoin de moins de garanties pour un accès pendant l'exécution, même avec plusieurs threads, vous pouvez utiliser un mode d'accès moins restrictif. À savoir, vous pouvez, par exemple, faire atomic_fetch_and_add_explicit(&var, 1, memory_order_relaxed). Cela garantit toujours que votre accès est indivisible (l'une des propriétés que vous voulez d'un atome) mais il ne garantit pas quand un autre thread voit la valeur mise à jour. Mais de manière générale, si les accès atomiques sont critiques sur le plan des performances, vous faites quelque chose de mal. Donc, avant d'essayer des relations sémantiquement difficiles avec les atomiques, comparez votre code et voyez si c'est vraiment un goulot d'étranglement. Si c'est le cas, pensez d'abord à un moyen de modifier votre algorithme, par exemple en effectuant davantage de calculs dans des variables locales qui ne sont pas soumises à des races. Seulement si tout cela ne vous donne pas la performance que vous voulez, jetez un oeil sur les différentes sémantiques de mémoire que C11 offre.