std:: atomic_thread_fence
|
Défini dans l'en-tête
<atomic>
|
||
|
extern
"C"
void
atomic_thread_fence
(
std::
memory_order
order
)
noexcept
;
|
(depuis C++11) | |
Établit l'ordre de synchronisation de la mémoire des accès non atomiques et atomiques relâchés, comme indiqué par order , sans opération atomique associée. Notez cependant qu'au moins une opération atomique est requise pour configurer la synchronisation, comme décrit ci-dessous.
Table des matières |
Synchronisation barrière-atomique
Une barrière de libération
F
dans le thread
A
se synchronise-avec une
opération d'acquisition
atomique
Y
dans le thread
B
, si
-
il existe un stockage atomique
X(avec n'importe quel ordre mémoire), -
Ylit la valeur écrite parX(ou la valeur qui serait écrite par la séquence de libération dirigée parXsiXétait une opération de libération), -
Fest séquencé-avantXdans le threadA.
Dans ce cas, tous les stockages non atomiques et atomiques relâchés qui sont
séquencés-avant
F
dans le thread
A
vont
se-produire-avant
tous les chargements non atomiques et atomiques relâchés depuis les mêmes emplacements effectués dans le thread
B
après
Y
.
Synchronisation par barrière atomique
Une opération atomique de
libération
X
dans le thread
A
se synchronise-avec une barrière d'acquisition
F
dans le thread
B
, si
-
il existe une lecture atomique
Y(avec n'importe quel ordre mémoire), -
Ylit la valeur écrite parX(ou par la séquence de libération menée par X ), -
Yest séquencé-avantFdans le threadB.
Dans ce cas, tous les stockages non atomiques et atomiques relâchés qui sont
séquencés-avant
X
dans le thread
A
vont
se-produire-avant
tous les chargements non atomiques et atomiques relâchés depuis les mêmes emplacements effectués dans le thread
B
après
F
.
Synchronisation fence-fence
Une barrière de libération
FA
dans le thread
A
se synchronise-avec une barrière d'acquisition
FB
dans le thread
B
, si
-
il existe un objet atomique
M, -
il existe une écriture atomique
X(avec n'importe quel ordre mémoire) qui modifieMdans le threadA, -
FAest séquencé-avantXdans le threadA, -
il existe une lecture atomique
Y(avec n'importe quel ordre mémoire) dans le threadB, -
Ylit la valeur écrite parX(ou la valeur qui serait écrite par la séquence de libération menée parXsiXétait une opération de libération), -
Yest séquencé-avantFBdans le threadB.
Dans ce cas, tous les stockages non atomiques et atomiques relâchés qui sont
séquencés-avant
FA
dans le thread
A
seront
réalisés-avant
tous les chargements non atomiques et atomiques relâchés depuis les mêmes emplacements effectués dans le thread
B
après
FB
.
Selon la valeur du paramètre order , les effets de cet appel sont :
- Lorsque order == std:: memory_order_relaxed , il n'y a aucun effet.
- Lorsque order == std:: memory_order_acquire ou order == std:: memory_order_consume , constitue une barrière d'acquisition.
- Lorsque order == std:: memory_order_release , constitue une barrière de libération.
- Lorsque order == std:: memory_order_acq_rel , constitue à la fois une barrière de libération et une barrière d'acquisition.
- Lorsque order == std:: memory_order_seq_cst , constitue une barrière d'acquisition et de libération à ordonnancement séquentiellement cohérent.
Paramètres
| order | - | l'ordre de mémoire exécuté par cette barrière |
Notes
Sur x86 (incluant x86-64),
atomic_thread_fence
n'émet aucune instruction CPU et n'affecte que le déplacement du code à la compilation, sauf pour
std
::
atomic_thread_fence
(
std::
memory_order_seq_cst
)
.
atomic_thread_fence
impose des contraintes de synchronisation plus fortes qu'une opération de stockage atomique avec le même
std::memory_order
. Alors qu'une opération de stockage-release atomique empêche toutes les lectures et écritures précédentes de passer au-delà du stockage-release, une
atomic_thread_fence
avec l'ordonnancement
std::
memory_order_release
empêche toutes les lectures et écritures précédentes de passer au-delà de tous les stockages ultérieurs.
La synchronisation fence-fence peut être utilisée pour ajouter de la synchronisation à une séquence de plusieurs opérations atomiques relaxées, par exemple :
// Global std::string computation(int); void print(std::string); std::atomic<int> arr[3] = {-1, -1, -1}; std::string data[1000]; //données non atomiques // Thread A, calcule 3 valeurs. void ThreadA(int v0, int v1, int v2) { // assert(0 <= v0, v1, v2 < 1000); data[v0] = computation(v0); data[v1] = computation(v1); data[v2] = computation(v2); std::atomic_thread_fence(std::memory_order_release); std::atomic_store_explicit(&arr[0], v0, std::memory_order_relaxed); std::atomic_store_explicit(&arr[1], v1, std::memory_order_relaxed); std::atomic_store_explicit(&arr[2], v2, std::memory_order_relaxed); } // Thread B, affiche entre 0 et 3 valeurs déjà calculées. void ThreadB() { int v0 = std::atomic_load_explicit(&arr[0], std::memory_order_relaxed); int v1 = std::atomic_load_explicit(&arr[1], std::memory_order_relaxed); int v2 = std::atomic_load_explicit(&arr[2], std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); // v0, v1, v2 peuvent s'avérer être -1, certains ou tous. // Sinon, il est sûr de lire les données non atomiques grâce aux barrières : if (v0 != -1) print(data[v0]); if (v1 != -1) print(data[v1]); if (v2 != -1) print(data[v2]); }
Exemple
Scannez un tableau de boîtes aux lettres et traitez uniquement celles qui nous sont destinées, sans synchronisation inutile. Cet exemple utilise une synchronisation par barrière atomique.
const int num_mailboxes = 32; std::atomic<int> mailbox_receiver[num_mailboxes]; std::string mailbox_data[num_mailboxes]; // Les threads écrivains mettent à jour les données partagées non atomiques // puis mettent à jour mailbox_receiver[i] comme suit : mailbox_data[i] = ...; std::atomic_store_explicit(&mailbox_receiver[i], receiver_id, std::memory_order_release); // Le thread lecteur doit vérifier tous les mailbox[i], mais ne doit se synchroniser qu'avec un seul. for (int i = 0; i < num_mailboxes; ++i) if (std::atomic_load_explicit(&mailbox_receiver[i], std::memory_order_relaxed) == my_id) { // se synchroniser avec un seul écrivain std::atomic_thread_fence(std::memory_order_acquire); // garantie d'observer tout ce qui a été fait dans le thread écrivain // avant le atomic_store_explicit() do_work(mailbox_data[i]); }
Voir aussi
|
(C++11)
|
définit les contraintes d'ordonnancement mémoire pour l'opération atomique donnée
(enum) |
|
(C++11)
|
barrière mémoire entre un thread et un gestionnaire de signal exécuté dans le même thread
(function) |
|
Documentation C
pour
atomic_thread_fence
|
|