std::atomic_thread_fence
| 在标头 <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 的所有非原子的和宽松原子的存储操作,将先发生于线程 B 中 Y 之后发生的所有来自同一位置的非原子的和宽松原子的加载操作。
原子-栅栏同步
满足以下条件时,线程 A 中的原子释放操作 (release operation) X 同步于线程 B 中的获得栅栏 F:
- 存在(以任何内存定序的)原子读取操作
Y, -
Y读取X(或X所引领的释放序列)所写入的值, -
Y在线程B中先序于F。
此情况下,线程 A 中先序于 X 的所有非原子的和宽松原子的存储操作,将先发生于线程 B 中 F 之后发生的所有来自同一位置的非原子的和宽松原子的加载操作。
栅栏-栅栏同步
满足以下条件时,线程 A 中的释放栅栏 FA 同步于线程 B 中的获得栅栏 FB:
- 存在原子对象
M, - 线程
A中存在(以任何内存定序)修改M的原子写入操作X, - 线程
A中FA先序于X, - 线程
B中存在(以任何内存定序的)原子读取操作Y, -
Y读取X所写入的值(或若X是释放操作时为X所引领的释放序列所写入的值), - 线程
B中Y先序于FB。
此情况下,线程 A 中先序于 FA 的所有非原子的和宽松原子的存储操作,将先发生于线程 B 中 FB 之后发生的所有来自同一位置的非原子的和宽松原子的加载操作。
根据 order 参数的值,此调用的效果是:
- 当 order == std::memory_order_relaxed 时,无效果。
- 当 order == std::memory_order_acquire 或者 order == std::memory_order_consume 时,作为获取栅栏。
- 当 order == std::memory_order_release 时,作为释放栅栏。
- 当 order == std::memory_order_acq_rel 时,同时作为释放栅栏和获取栅栏。
- 当 order == std::memory_order_seq_cst 时,作为序列一致定序的释放栅栏和获取栅栏。
参数
| 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]); }
参阅
| (C++11) |
为给定的原子操作定义内存定序约束 (枚举) |
| (C++11) |
线程与执行于同一线程的信号处理函数间的栅栏 (函数) |