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
Definiert in Header <atomic>
extern "C" void atomic_thread_fence ( std:: memory_order order ) noexcept ;
(seit C++11)

Legt die Speichersynchronisationsreihenfolge von nicht-atomaren und entspannten atomaren Zugriffen fest, wie durch order angegeben, ohne eine zugehörige atomare Operation. Beachten Sie jedoch, dass mindestens eine atomare Operation erforderlich ist, um die Synchronisation einzurichten, wie unten beschrieben.

Inhaltsverzeichnis

Fence-Atomare Synchronisierung

Ein Release-Fence F in Thread A synchronisiert mit atomarer Acquire-Operation Y in Thread B , wenn

  • es existiert ein atomarer Store X (mit beliebiger Speicherreihenfolge),
  • Y liest den von X geschriebenen Wert (oder den Wert, der von einer Release-Sequenz mit Startpunkt X geschrieben würde, falls X eine Release-Operation wäre),
  • F ist vor X in Thread A sequenziert.

In diesem Fall werden alle nicht-atomaren und entspannten atomaren Speichervorgänge, die sequenced-before F in Thread A sind, happen-before allen nicht-atomaren und entspannten atomaren Ladevorgängen von denselben Speicherorten in Thread B nach Y .

Atomare-Fence-Synchronisation

Eine atomare Release-Operation X in Thread A synchronisiert sich mit einem Acquire-Fence F in Thread B , wenn

  • es existiert ein atomarer Lesevorgang Y (mit beliebiger Speicherreihenfolge),
  • Y liest den von X geschriebenen Wert (oder von der von X angeführten Release-Sequenz ),
  • Y ist in Thread B vor F sequenziert.

In diesem Fall werden alle nicht-atomaren und relaxed atomaren Speichervorgänge, die sequenced-before X in Thread A sind, happen-before allen nicht-atomaren und relaxed atomaren Ladevorgängen von denselben Speicherstellen in Thread B nach F .

Fence-Fence-Synchronisation

Ein Release-Fence FA in Thread A synchronisiert mit einem Acquire-Fence FB in Thread B , wenn

  • es existiert ein atomares Objekt M ,
  • es existiert ein atomarer Schreibzugriff X (mit beliebiger Speicherreihenfolge), der M in Thread A modifiziert,
  • FA ist sequenced-before X in Thread A ,
  • es existiert ein atomarer Lesezugriff Y (mit beliebiger Speicherreihenfolge) in Thread B ,
  • Y liest den von X geschriebenen Wert (oder den Wert, der durch die release sequence headed by X geschrieben würde, falls X eine release-Operation wäre),
  • Y ist sequenced-before FB in Thread B .

In diesem Fall werden alle nicht-atomaren und entspannten atomaren Speichervorgänge, die sequenced-before FA in Thread A sind, happen-before allen nicht-atomaren und entspannten atomaren Ladevorgängen von denselben Speicherstellen in Thread B nach FB .

Abhängig vom Wert des order Parameters sind die Auswirkungen dieses Aufrufs:

Parameter

order - die von diesem Fence ausgeführte Speicherreihenfolge

Hinweise

Auf x86 (einschließlich x86-64), atomic_thread_fence Funktionen geben keine CPU-Befehle aus und beeinflussen nur die Codebewegung zur Compile-Zeit, außer für std :: atomic_thread_fence ( std:: memory_order_seq_cst ) .

atomic_thread_fence erlegt stärkere Synchronisierungsbeschränkungen auf als eine atomare Speicheroperation mit demselben std::memory_order . Während eine atomare Store-Release-Operation verhindert, dass alle vorangehenden Lese- und Schreiboperationen über die Store-Release-Operation hinaus verschoben werden, verhindert ein atomic_thread_fence mit std:: memory_order_release -Reihenfolge, dass alle vorangehenden Lese- und Schreiboperationen über alle nachfolgenden Speicheroperationen hinaus verschoben werden.

Fence-Fence-Synchronisation kann verwendet werden, um einer Sequenz mehrerer entspannter atomarer Operationen Synchronisation hinzuzufügen, zum Beispiel:

// Global
std::string computation(int);
void print(std::string);
std::atomic<int> arr[3] = {-1, -1, -1};
std::string data[1000]; //nicht-atomare Daten
// Thread A, berechnet 3 Werte.
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, gibt zwischen 0 und 3 bereits berechnete Werte aus.
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 könnten sich als -1 herausstellen, einige oder alle davon.
//  Andernfalls ist es aufgrund der Fences sicher, die nicht-atomaren Daten zu lesen:
    if (v0 != -1)
        print(data[v0]);
    if (v1 != -1)
        print(data[v1]);
    if (v2 != -1)
        print(data[v2]);
}

Beispiel

Scannen Sie ein Array von Mailboxen und verarbeiten Sie nur die für uns bestimmten, ohne unnötige Synchronisierung. Dieses Beispiel verwendet Atomic-Fence-Synchronisierung.

const int num_mailboxes = 32;
std::atomic<int> mailbox_receiver[num_mailboxes];
std::string mailbox_data[num_mailboxes];
// Die Writer-Threads aktualisieren nicht-atomare gemeinsame Daten
// und aktualisieren dann mailbox_receiver[i] wie folgt:
mailbox_data[i] = ...;
std::atomic_store_explicit(&mailbox_receiver[i], receiver_id, std::memory_order_release);
// Der Reader-Thread muss alle mailbox[i] prüfen, muss aber nur mit einem synchronisieren.
for (int i = 0; i < num_mailboxes; ++i)
    if (std::atomic_load_explicit(&mailbox_receiver[i],
        std::memory_order_relaxed) == my_id)
    {
        // Synchronisiere nur mit einem Writer
        std::atomic_thread_fence(std::memory_order_acquire);
        // Garantiert, alles zu sehen, was im Writer-Thread
        // vor dem atomic_store_explicit() ausgeführt wurde
        do_work(mailbox_data[i]);
    }

Siehe auch

definiert Speicherreihenfolge-Einschränkungen für die gegebene atomare Operation
(enum)
Barriere zwischen einem Thread und einem Signal-Handler im selben Thread
(function)
C-Dokumentation für atomic_thread_fence