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
Определено в заголовочном файле <atomic>
extern "C" void atomic_thread_fence ( std:: memory_order order ) noexcept ;
(начиная с C++11)

Устанавливает порядок синхронизации памяти для неатомарных и релаксированных атомарных обращений, как указано в order , без связанной атомарной операции. Однако обратите внимание, что для настройки синхронизации требуется как минимум одна атомарная операция, как описано ниже.

Содержание

Синхронизация барьер-атомарная

Барьер освобождения F в потоке A синхронизируется с атомарной операцией захвата Y в потоке B , если

  • существует атомарная запись X (с любым порядком памяти),
  • Y читает значение, записанное X (или значение, которое было бы записано релиз-последовательностью во главе с X если бы X была операцией релиза),
  • F упорядочена перед X в потоке A .

В данном случае все неатомарные и релаксированные атомарные записи, которые упорядочены-перед F в потоке A будут происходить-перед всеми неатомарными и релаксированными атомарными чтениями из тех же локаций, выполняемыми в потоке B после Y .

Синхронизация с использованием атомарных барьеров

Атомарная операция освобождения X в потоке A синхронизируется с барьером захвата F в потоке B , если

В данном случае все неатомарные и релаксированные атомарные сохранения, которые упорядочены-перед X в потоке A будут происходить-перед всеми неатомарными и релаксированными атомарными загрузками из тех же областей памяти, выполненных в потоке B после F .

Синхронизация барьер-барьер

Релизный барьер FA в потоке A синхронизируется с аквирным барьером FB в потоке B , если

  • существует атомарный объект M ,
  • существует атомарная запись X (с любым порядком памяти), которая модифицирует M в потоке A ,
  • FA упорядочена-перед X в потоке A ,
  • существует атомарное чтение Y (с любым порядком памяти) в потоке B ,
  • Y читает значение, записанное X (или значение, которое было бы записано релиз-последовательностью во главе с X если бы X была релиз-операцией),
  • Y упорядочена-перед FB в потоке B .

В данном случае все неатомарные и релаксированные атомарные сохранения, которые упорядочены-перед FA в потоке A будут происходить-перед всеми неатомарными и релаксированными атомарными загрузками из тех же локаций, выполненных в потоке B после FB .

В зависимости от значения параметра order , последствия этого вызова следующие:

Параметры

order - порядок памяти, выполняемый этим барьером

Примечания

На архитектуре x86 (включая x86-64), atomic_thread_fence функции не генерируют инструкций процессора и влияют только на перемещение кода во время компиляции, за исключением std :: atomic_thread_fence ( std:: memory_order_seq_cst ) .

atomic_thread_fence накладывает более строгие ограничения синхронизации, чем атомарная операция сохранения с тем же std::memory_order . В то время как атомарная операция сохранения-освобождения предотвращает перемещение всех предыдущих операций чтения и записи после сохранения-освобождения, atomic_thread_fence с порядком std:: memory_order_release предотвращает перемещение всех предыдущих операций чтения и записи после всех последующих операций сохранения.

Синхронизация по типу fence-fence может использоваться для добавления синхронизации к последовательности из нескольких ослабленных атомарных операций, например:

// Глобальные переменные
std::string computation(int);
void print(std::string);
std::atomic<int> arr[3] = {-1, -1, -1};
std::string data[1000]; // неатомарные данные
// Поток A, вычисляет 3 значения.
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);
}
// Поток B, выводит от 0 до 3 уже вычисленных значений.
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 могут оказаться равными -1, некоторые или все из них.
//  В противном случае безопасно читать неатомарные данные благодаря барьерам:
    if (v0 != -1)
        print(data[v0]);
    if (v1 != -1)
        print(data[v1]);
    if (v2 != -1)
        print(data[v2]);
}

Пример

Просканируйте массив почтовых ящиков и обработайте только те, которые предназначены для нас, без излишней синхронизации. В этом примере используется синхронизация atomic-fence.

const int num_mailboxes = 32;
std::atomic<int> mailbox_receiver[num_mailboxes];
std::string mailbox_data[num_mailboxes];
// Потоки-писатели обновляют неатомарные разделяемые данные
// и затем обновляют mailbox_receiver[i] следующим образом:
mailbox_data[i] = ...;
std::atomic_store_explicit(&mailbox_receiver[i], receiver_id, std::memory_order_release);
// Потоку-читателю нужно проверить все mailbox[i], но синхронизироваться только с одним.
for (int i = 0; i < num_mailboxes; ++i)
    if (std::atomic_load_explicit(&mailbox_receiver[i],
        std::memory_order_relaxed) == my_id)
    {
        // синхронизация только с одним писателем
        std::atomic_thread_fence(std::memory_order_acquire);
        // гарантированно видит все операции, выполненные в потоке-писателе
        // до atomic_store_explicit()
        do_work(mailbox_data[i]);
    }

Смотрите также

определяет ограничения упорядочения памяти для данной атомарной операции
(enum)
барьер между потоком и обработчиком сигнала, выполняемым в том же потоке
(function)
C documentation для atomic_thread_fence