std::atomic_thread_fence

来自cppreference.com
< cpp‎ | atomic
 
 
并发支持库
线程
(C++11)
(C++20)
this_thread 命名空间
(C++11)
(C++11)
(C++11)
协作式取消
互斥
(C++11)
通用锁管理
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
条件变量
(C++11)
信号量
闩与屏障
(C++20)
(C++20)
未来体
(C++11)
(C++11)
(C++11)
(C++11)
安全回收
(C++26)
风险指针
原子类型
(C++11)
(C++20)
原子类型的初始化
(C++11)(C++20 弃用)
(C++11)(C++20 弃用)
内存定序
(C++11)(C++26 弃用)
atomic_thread_fence
(C++11)
原子操作的自由函数
原子标志的自由函数
 
在标头 <atomic> 定义
extern "C" void atomic_thread_fence( std::memory_order order ) noexcept;
(C++11 起)

建立以 order 指示的非原子和宽松原子访问的内存同步定序,而无需关联的原子操作。但需要至少一个原子操作来建立同步,如下所述。

栅栏-原子同步

满足以下条件时,线程 A 中的释放栅栏 F 同步于线程 B 中的原子获得操作 (acquire operation) Y

  • 存在(以任何内存定序的)原子存储操作 X
  • Y 读取 X 所写入的值(或若 X 是释放操作则为 X 所引领的释放序列所写入的值),
  • F 在线程 A 中先序于 X

此情况下,线程 A先序于 F 的所有非原子的和宽松原子的存储操作,将先发生于线程 BY 之后发生的所有来自同一位置的非原子的和宽松原子的加载操作。

原子-栅栏同步

满足以下条件时,线程 A 中的原子释放操作 (release operation) X 同步于线程 B 中的获得栅栏 F

  • 存在(以任何内存定序的)原子读取操作 Y
  • Y 读取 X(或 X 所引领的释放序列)所写入的值,
  • Y 在线程 B 中先序于 F

此情况下,线程 A先序于 X 的所有非原子的和宽松原子的存储操作,将先发生于线程 BF 之后发生的所有来自同一位置的非原子的和宽松原子的加载操作。

栅栏-栅栏同步

满足以下条件时,线程 A 中的释放栅栏 FA 同步于线程 B 中的获得栅栏 FB

  • 存在原子对象 M
  • 线程 A 中存在(以任何内存定序)修改 M 的原子写入操作 X
  • 线程 AFA 先序于 X
  • 线程 B 中存在(以任何内存定序的)原子读取操作 Y
  • Y 读取 X 所写入的值(或若 X 是释放操作时为 X 所引领的释放序列所写入的值),
  • 线程 BY 先序于 FB

此情况下,线程 A先序于 FA 的所有非原子的和宽松原子的存储操作,将先发生于线程 BFB 之后发生的所有来自同一位置的非原子的和宽松原子的加载操作。

根据 order 参数的值,此调用的效果是:

参数

order - 此栅栏所执行的内存定序

注解

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

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。
//  其他情况下读取非原子数据是安全的,因为栅栏:
    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]);
    }

参阅

为给定的原子操作定义内存定序约束
(枚举)
线程与执行于同一线程的信号处理函数间的栅栏
(函数)