繁体   English   中英

C++ std::memory_order_relaxed 混乱

[英]C++ std::memory_order_relaxed confusion

我正在阅读 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 */

由于线程不需要跨系统同步,因此本示例中的任一断言实际上都可能失败。

好的,这也很简单,让我们继续..

-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)

断言不能失败。 一旦线程 2 看到 2 的存储,它就不能再看到值 1。这可以防止将一个变量的松弛加载与可能别名的不同引用的松弛加载合并。

这让我很困惑,为什么 y 不能加载值 2 而 z 加载值 1(并导致断言失败),因为排序在线程 1 中不同步?

松弛排序是相对于其他内存访问的操作顺序而言的,而不是相对于被松弛修改的原子的排序。 在您的第一种情况下,您可以在x中看到 10 这一事实对于y的值没有任何意义。 反之亦然。

但是你的第二种情况不同,因为它影响同一个原子对象。

[intro.races]/10告诉我们,在一个线程中,如果一个操作在另一个之前被排序,那么该操作“发生在”另一个之前。 [intro.races]/14-17 概述了关于原子的以下行为

前面的四个一致性要求有效地禁止编译器将原子操作重新排序为单个对象,即使这两个操作都是宽松加载。

这就是你在这里所拥有的。 所有的修改都发生在同一个对象上,所以它们必须以某种顺序发生。 即使无法准确确定该顺序,该顺序也必须尊重代码的“先发生”关系。

线程 1 的两个操作按“先发生”关系排序。 并且线程 2 的操作本身是由“发生在之前”关系排序的。

由于它们都作用于同一个原子对象,如果y的值是 2,那么它一定是“发生在” x被设置为 2 之后。所以x的访问顺序一定是“x = 1, x = 2,读取 x"。 由于最后一次读取x发生在第一次读取x ,所以它得到的值不能为 1。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM