Namespaces
Variants

std:: atomic_thread_fence

From cppreference.net
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
Generic lock management
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
Safe reclamation
Hazard pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11) (deprecated in C++20)
(C++11) (deprecated in C++20)
Memory ordering
(C++11) (deprecated in C++26)
atomic_thread_fence
(C++11)
Free functions for atomic operations
Free functions for atomic flags
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),
  • Y lit la valeur écrite par X (ou la valeur qui serait écrite par la séquence de libération dirigée par X si X était une opération de libération),
  • F est séquencé-avant X dans le thread A .

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),
  • Y lit la valeur écrite par X (ou par la séquence de libération menée par X ),
  • Y est séquencé-avant F dans le thread B .

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 modifie M dans le thread A ,
  • FA est séquencé-avant X dans le thread A ,
  • il existe une lecture atomique Y (avec n'importe quel ordre mémoire) dans le thread B ,
  • Y lit la valeur écrite par X (ou la valeur qui serait écrite par la séquence de libération menée par X si X était une opération de libération),
  • Y est séquencé-avant FB dans le thread B .

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 :

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

définit les contraintes d'ordonnancement mémoire pour l'opération atomique donnée
(enum)
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