std:: atomic_thread_fence
|
Определено в заголовочном файле
<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
, если
-
существует атомарное чтение
Y(с любым порядком памяти), -
Yчитает значение, записанное операциейX(или релиз-последовательностью, возглавляемой X ), -
Yупорядочено перед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 == std:: memory_order_relaxed , эффекты отсутствуют.
- Когда order == std:: memory_order_acquire или order == std:: memory_order_consume , является барьером захвата (acquire fence).
- Когда order == std:: memory_order_release , является барьером освобождения (release fence).
- Когда order == std:: memory_order_acq_rel , является одновременно барьером освобождения и захвата.
- Когда order == std:: memory_order_seq_cst , является барьером последовательно-согласованного упорядочивания с захватом и освобождением.
Параметры
| 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]); }
Смотрите также
|
(C++11)
|
определяет ограничения упорядочения памяти для данной атомарной операции
(enum) |
|
(C++11)
|
барьер между потоком и обработчиком сигнала, выполняемым в том же потоке
(function) |
|
C documentation
для
atomic_thread_fence
|
|