c++ - 为什么 'acquire/release'在c++11中不能保证顺序一致性?

标签 c++ c++11 memory-model stdatomic happens-before

-Thread 1-                
y.store (20, memory_order_release); 
x.store (10, memory_order_release);

-Thread 2-
if (x.load(memory_order_acquire) == 10) {
  assert (y.load(memory_order_acquire) == 20);
  y.store (10, memory_order_release)
}

-Thread 3-
if (y.load(memory_order_acquire) == 10) {
  assert (x.load(memory_order_acquire) == 10);
}

GCC Atomic Wiki “Overall Summary”段落说,上面的代码assert(x.load(memory_order_acquire))可以失败。但我不明白为什么?

我的理解是:

  1. 由于 acquire 屏障,Thread3 无法LoadLoad 重新排序。
  2. 由于释放 障碍,Thread1 无法StoreStore 重新排序。
  3. 当 Thread2 read(x)->10 时,x 必须从 storebuffer 刷新到 Thread1 的缓存中,这样每个线程都知道 x 的值发生了变化,例如使缓存行无效。
  4. Thread3 使用Acquire 屏障,因此它可以看到 x(10)。

最佳答案

这是一个糟糕的例子,但我想它确实说明了松散的原子是多么令人费解。

[介绍.执行]p9:

Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

[atomics.order]p2:

An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and takes its value from any side effect in the release sequence headed by A.

因此,显示的评估通过 sequenced-before 和 synchronized-with 关系链接在一起:

Thread 1                   Thread 2              Thread 3

y.store(20)
   |
   | s.b.
   V           s.w.
x.store(10)  -------->  x.load() == 10
                               |
                               | s.b.
                               V      s.w.
                        y.store(10) --------> y.load() == 10
                                                  |
                                                  | s.b.
                                                  V
                                              x.load() == ?

因此链中的每个评估发生在下一个之前(参见 [intro.races]p9-10)。

[intro.races]p15,

If a value computation A of an atomic object M happens before a value computation B of M, and A takes its value from a side effect X on M, then the value computed by B shall either be the value stored by X or the value stored by a side effect Y on M, where Y follows X in the modification order of M.

这里,A 是线程 2 中取值为 10 的负载,B 是线程 3 中的负载(在断言中)。由于 A 发生在 B 之前,并且 x 没有其他副作用,因此 B 也必须读取 10。


Herb Sutter 有一个更简单的例子 on his blog :

T1: x = 1;
T2: y = 1;
T3: if( x == 1 && y == 0 ) puts("x first");
T4: if( y == 1 && x == 0 ) puts("y first");

您绝对需要顺序一致性来保证最多打印一行。

关于c++ - 为什么 'acquire/release'在c++11中不能保证顺序一致性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50441014/

相关文章:

c++ - Swift - C++ 的 CRC8 计算转换

c++ - 如何安全地从流中读取无符号整数?

c++ - 给定 int **p1 和 const int**p2 p1 == p2 格式正确吗?

c++ - 可变模板构造函数和复制构造函数

c++ - 关于 shared_ptr 析构函数中实现错误的困惑

c++ - 引用计数的发布-消费排序

c# - 内存型号 : preventing store-release and load-acquire reordering

c++ - 为什么 'this' 是指针而不是引用?

c++ - 特定文件夹保存和读取

c++ - 无法使用 cout << method() 打印字符串;其中 method() 返回一个字符串