Edu*_*yan 5 c++ multithreading synchronization atomic
我正在阅读GCC Wiki 上关于 C++ 内存障碍(及其很棒)的这篇文章。
在我到达这一点之前,它非常简单:
相反的方法是 std::memory_order_relaxed。该模型通过消除发生之前的限制,允许更少的同步。这些类型的原子操作也可以对它们执行各种优化,例如删除死存储和共用。所以在前面的例子中:
-Thread 1-
y.store (20, memory_order_relaxed)
x.store (10, memory_order_relaxed)
-Thread 2-
if (x.load (memory_order_relaxed) == 10)
{
assert (y.load(memory_order_relaxed) == 20) /* assert A */
y.store (10, memory_order_relaxed)
}
-Thread 3-
if (y.load (memory_order_relaxed) == 10)
assert (x.load(memory_order_relaxed) == 10) /* assert B */
Run Code Online (Sandbox Code Playgroud)
由于线程不需要跨系统同步,因此本示例中的任一断言实际上都可能失败。
好的,这也很简单,让我们继续..
-Thread 1-
x.store (1, memory_order_relaxed)
x.store (2, memory_order_relaxed)
-Thread 2-
y = x.load (memory_order_relaxed)
z = x.load (memory_order_relaxed)
assert (y <= z)
Run Code Online (Sandbox Code Playgroud)
断言不能失败。一旦线程 2 看到 2 的存储,它就不能再看到值 1。这可以防止将一个变量的松弛加载跨可能别名的不同引用的松弛加载合并。
这让我感到困惑,为什么 y 不能加载值 2 而 z 加载值 1(并导致断言失败),因为排序在线程 1 中不同步?
松弛排序是相对于其他内存访问的操作顺序而言的,而不是相对于被松弛修改的原子的排序。在您的第一种情况下,您可以看到 10 in 的事实x对于 的值没有任何意义y。反之亦然。
但是你的第二种情况不同,因为它影响同一个原子对象。
[intro.races]/10告诉我们,在一个线程中,如果一个操作在另一个之前被排序,那么该操作“发生在”另一个之前。和[intro.races] / 14-17大纲关于原子能以下行为:
前面的四个一致性要求有效地禁止编译器将原子操作重新排序为单个对象,即使这两个操作都是宽松加载。
这就是你在这里所拥有的。所有的修改都发生在同一个对象上,所以它们必须以某种顺序发生。即使无法准确确定该顺序,该顺序也必须尊重代码的“先发生”关系。
线程 1 的两个操作按“先发生”关系排序。并且线程 2 的操作本身是由“发生在之前”关系排序的。
由于它们都作用于同一个原子对象,如果y得到的值为 2,那么它一定是“发生在”x被设置为 2之后。所以访问的顺序x一定是“x = 1,x = 2,读 x”。由于最后一次读取x发生在第一次读取之后x,所以它得到的值不能为 1。