C ++ std atomic

C ++ std atomic

Nous utilisons le «std atomic» si nous voulons conserver l'atomicité de l'opération en C++. L'atomicité est le concept que nous pouvons gérer lorsque nous travaillons dans des opérations / applications multithreading où même les fonctions simples telles que la lecture et l'écriture dans l'exécution simultanément peuvent créer des problèmes ou des comportements non définis dans le code. Pour faire face à une telle situation, C ++ a défini la bibliothèque «Std :: Atomic» qui garantit une cohérence suffisamment séquentielle pour l'exécution de la lecture et de l'écriture pour divers objets distincts qui décrivent le comportement bien défini. Si un fil écrit à un moment donné, l'autre fil se lit à ce moment-là.

Procédure:

Cet article explorera ce qu'est l'atomicité et comment nous pouvons utiliser les concepts de Std Atomic pour faire face aux comportements indéfinis dans nos codes. Nous discuterons des différentes fonctions de la std atomique et implémenterons les différents exemples pour STD atomique. La fonction sous l'atomique std que nous implémenterons sur les différents exemples est donnée comme suit:

  • Lecture et écriture la plus simple des valeurs
  • Libérer et acquérir la commande avec la mémoire (modèle)
  • Modèle d'échange
  • Opération de récupération

Lecture et écriture la plus simple des valeurs

Créons une application multithread dans cet exemple où nous créons deux threads: un thread pour lire les valeurs et l'autre thread pour écrire les valeurs. À l'aide de cet exemple, nous essaierons d'obtenir le concept d'atomique STD, quels sont les comportements indéfinis tout en exécutant les applications multithread et comment l'atomique std élimine les comportements indéfinis.

Pour cela, nous entamons simplement le code en attribuant deux valeurs différentes à deux variables différentes de type entier. Tout d'abord, nous initialisons la variable «A» et «B» avec les types de données entiers. Ensuite, nous créons la fonction qui est écrite dans le vide. Dans cette fonction, nous attribuons les valeurs à la fois à «a» et à «b», e.g. 25 et 20, respectivement.

Ensuite, nous créons la fonction de lecture. Dans la fonction de lecture, les valeurs de «A» et «B» sont lues avec le «std :: cout <

Sortir:

La sortie de cet exemple représente le comportement non défini de l'application car la sortie du code est soit 0 ou 10. Cela s'est produit parce que les threads s'exécutaient simultanément et la commande de lecture aurait pu être effectuée lors de l'exécution de la commande d'écriture. De cette façon, nous avons obtenu un résultat incomplet dans la sortie.

L'atomique std peut résoudre ce problème et peut rendre les comportements non définis dans l'application bien définie. Pour implémenter cela, nous faisons simplement un petit changement tout en initialisant et en définissant les valeurs et les types de données des variables définies à l'aide de «std :: atomic». Nous définissons la variable «A» et «B» comme les variables atomiques par «STD :: Nom de la variable atomique». Nous faisons également un petit changement dans la fonction d'écriture où, auparavant, nous avons simplement attribué les valeurs à A et B en utilisant l'opérateur d'attribution «=». Mais ici, nous attribuons les valeurs en utilisant le «nom de variable. Méthode de stockage (valeur) ”. Nous utilisons le «nom variable. charge () ”dans la fonction de lecture. Le reste est le même que dans l'exemple précédent.

Les valeurs de la sortie sont lues et écrites de manière bien définie. Et le multithreading est également pris en charge ici.

Libérer et acquérir la commande avec la mémoire (modèle)

Le modèle de mémoire peut avoir un impact énorme sur les fonctions de lecture et d'écriture du std atomique. Le modèle de mémoire est une fonction par défaut qui s'assure de la cohérence de commande séquentielle. L'un des modèles atomiques les plus intéressants est le modèle de version et d'acquisition où nous pouvons stocker la version de commande mémoire pour le premier thread et l'ordre mémoire acquis pour le deuxième fil, ce qui signifie que tout magasin / écriture atomique ou non atomique est effectué D'abord dans le premier fil avant le deuxième fil, je.e. charger.

Ainsi, dans l'exemple, nous pouvons même changer la seule variable atomique «A» à non atomique et la deuxième variable «B» est maintenue atomique. Dans la fonction d'écriture, nous stockons la variable non atomique «A» simplement en lui attribuant n'importe quelle valeur, e.g. 30. Et nous stockons correctement la valeur de la variable atomique «B» en utilisant «B. Store (valeur, std :: Memory_Order_release) ". La même chose se fait également dans la fonction de lecture où nous utilisons le «std :: cout <

Sortir:

L'atomicité de l'opération est toujours maintenue avec la version et le modèle de mémoire acquis même lorsque nous avions une variable X non atomique. Cela s'est produit à cause des y (atomique.magasin) qui s'assurait de la maintenance de la cohérence séquentielle.

Modèle d'échange

Échange signifie lorsque nous échangeons la valeur d'une variable (atomique) à une autre valeur. En échange, la valeur est d'abord échangée, puis la valeur précédente qui est échangée par la nouvelle est renvoyée. Une fois la valeur échangée, elle réfléchit à chaque opération ultérieure à cette valeur. Mise en œuvre de cet échange de variables atomiques à l'aide d'un exemple.

Dans cet exemple, nous introduisons d'abord la variable atomique globale foobar qui a une certaine valeur égale à «15». Dans l'ensemble, nous faisons un thread en tant que Thread1 et lui attribuons une valeur entière égale à 2. Ensuite, dans la boucle FOR, nous avons réglé l'index de 0 à 100 fois. Ensuite, nous remplaçons la valeur de la variable FOOBAR à 2 en utilisant «Foobar. valeur d'échange)". Après cela, nous sortons de la boucle et chargeons la valeur de la variable FOOBAR pour l'imprimer. Après avoir chargé la valeur FOOBAR, nous échangeons désormais sa valeur avec 18 par le «.Échange (valeur à remplacer par) "Méthode. Puis encore, chargez les valeurs de Foobar et affichez-les à l'aide de la méthode d'impression.

Ici, dans cet exemple, le thread doit échanger les valeurs pour des centaines de fois et la valeur de Foobar est échangée de 15 à 28. Toute opération après cet échange renvoie la même valeur que celle que l'on peut voir dans la sortie.

Aller chercher

Fetch est le même que la fonction d'échange qui écrit les valeurs et renvoie les valeurs précédemment récupérées. Cette opération récupère la valeur qui est stockée avant que toute opération ne soit appliquée. Maintenant, nous mettons en œuvre le soulagement d'addition et de récupération de récupération dans cet exemple. Nous définissons une variable atomique avec le type de données non signé comme «compte» et initialisons le nombre avec zéro. Ensuite, nous créons deux fonctions - une pour Fetch Add et une autre pour récupérer. Nous exécutons le compteur de l'incrément 1 pour Add et Decmenment 1 pour soustraire dans ces deux fonctions. Ensuite, nous imprimons ces valeurs à la fois des fonctions fetch_add et fetch_sub dans le principal.

La fonction fetch_add a renvoyé 0 et 1 comme valeurs précédentes avant l'incrément. De même, le fetch_sub est revenu 2 et 1 comme valeurs stockées précédemment avant la soustraction ou la décrémentation de celle.

Conclusion

Nous avons mis en œuvre les opérations de base dans «Std :: Atomic» dans cet article. Nous avons appris comment nous pouvons gérer les problèmes dans les applications multithreading à l'aide de l'atomique std. Nous avons implémenté les différents exemples en C ++ pour les différentes fonctions telles que Fetch, Exchange, lecture / écriture et modèle de mémoire du std atomique pour assurer la cohérence séquentielle et les comportements bien définis du code pour les applications multithread.