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 中与线程 B 中的原子 获取操作 Y 实现同步,如果

  • 存在一个原子存储操作 X (采用任意内存序),
  • Y 读取了由 X 写入的值(或者如果 X 是释放操作,则读取由 X 为首的释放序列 将要写入的值),
  • 在线程 A 中, F 按顺序先于 X 执行。

在此情况下,线程 A 中所有 顺序先于 F 的非原子操作与宽松原子存储操作,都将 先于 线程 B 中在 Y 之后对相同内存位置执行的所有非原子操作与宽松原子加载操作发生。

原子栅栏同步

当线程 A 中的原子 释放操作 X 与线程 B 中的获取栅栏 F 满足以下条件时,二者建立同步关系:

  • 存在一个原子读取操作 Y (采用任意内存序),
  • Y 读取了由 X (或由 以 X 为首的释放序列 )所写入的值,
  • 在线程 B 中, Y 按顺序先于 F 执行。

在此情况下,线程 A 中所有 顺序先于 X 的非原子操作与宽松原子存储操作,将会 先于 线程 B 中在 F 之后对相同内存位置执行的所有非原子操作与宽松原子加载操作发生。

栅栏-栅栏同步

释放栅栏 FA 在线程 A 中与获取栅栏 FB 在线程 B 中实现同步,当

  • 存在一个原子对象 M
  • 存在一个原子写操作 X (使用任意内存序)在线程 A 中修改 M
  • FA 在线程 A 中按顺序先于 X
  • 存在一个原子读操作 Y (使用任意内存序)在线程 B 中,
  • Y 读取由 X 写入的值(或者如果 X 是释放操作,则读取由 X 为首的释放序列 将写入的值),
  • Y 在线程 B 中按顺序先于 FB

在此情况下,线程 A 中所有 按序早于 FA 的非原子操作与宽松原子存储操作,将会 先于 线程 B 中在 FB 之后对相同内存位置执行的所有非原子操作与宽松原子加载操作发生。

根据 order 参数值的不同,此调用的效果如下:

参数

order - 此栅栏执行的内存排序方式

注释

在 x86(包括 x86-64)架构上, atomic_thread_fence 函数不会发出任何 CPU 指令,仅影响编译时代码移动,但 std :: atomic_thread_fence ( std:: memory_order_seq_cst ) 除外。

atomic_thread_fence 比具有相同 std::memory_order 的原子存储操作施加更强的同步约束。原子存储-释放操作会阻止所有先前的读写操作越过该存储-释放操作,而具有 std:: memory_order_release 顺序的 atomic_thread_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,部分或全部为-1
//  由于栅栏的存在,读取非原子数据是安全的:
    if (v0 != -1)
        print(data[v0]);
    if (v1 != -1)
        print(data[v1]);
    if (v2 != -1)
        print(data[v2]);
}

示例

扫描邮箱数组,仅处理目标为我们的邮箱,避免不必要的同步。 此示例使用原子栅栏同步机制。

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 文档 关于 atomic_thread_fence