-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段落“总结摘要”说,上面的代码assert(x.load(memory_order_acquire))
可以失败。但我不明白为什么?
我的理解是:
答案 0 :(得分:2)
这是一个不好的例子,虽然它确实说明了心灵变形放松原子可以如何,我想。
[intro.execution] P9:
与a相关的每个值计算和副作用 full-expression在每个值计算和side之前排序 与要评估的下一个完整表达相关联的效果。
[atomics.order] P2:
对原子执行释放操作的原子操作 A object M 与执行a的原子操作 B 同步 获取 M 上的操作并从中获取其中任何副作用的值 以 A 为首的发布序列。
因此,显示的评估通过先前排序和同步关系链接在一起:
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,
如果原子对象 M 的值计算 A 发生在值之前 M 的计算 B , A 从 M 上的副作用 X 获取其值, 那么 B 计算的值应该是 X 或者存储的值 副作用 Y 存储在 M 上的值,其中 Y 跟随 Y 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");
您绝对需要顺序一致性以确保最多打印一行。
答案 1 :(得分:0)
您的示例是多线程程序的特例,该程序仅使用原子对象进行同步,而不使用具有明显熵的信息进行通信:唯一写入的值充当里程碑。
这意味着任何商店:
该表单必须完全A.store (C, memory_order_release);
其中:
A
是一个原子对象C
是一个常量,一对(A,C)
独特地代表了程序行。
相反,每次加载:
表格必须准确
if (A.load(memory_order_acquire) == C) { ... }
没有else子句的地方。
读取特定值表示进度,并确定所有先前的副作用(在特定程序点之前)均已发生。一小类程序永远不会具有有趣的多线程行为,因为一切都是进步的功能:里程碑已通过,并且行为必须纯粹是顺序的。