【问题标题】:C++ std::memory_order_relaxed confusionC++ std::memory_order_relaxed 混淆
【发布时间】:2021-01-26 09:53:37
【问题描述】:

我正在阅读来自 GCC Wiki 的 this article 关于 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 中的排序不同步?

【问题讨论】:

  • 链接中缺少的段落说:“唯一的排序是,一旦在线程 2 中观察到来自线程 1 的变量的值,线程 2 就看不到“更早”该变量的值来自线程 1".

标签: c++ multithreading synchronization atomic


【解决方案1】:

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

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

[intro.races]/10 告诉我们,在线程中,如果一个操作在另一个操作之前排序,那么该操作“发生在”另一个之前。还有[intro.races]/14-17 outline the following behavior with regard to atomics

前面的四个一致性要求实际上不允许编译器对单个对象的原子操作重新排序,即使这两个操作都是宽松的负载。

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

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

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

【讨论】:

    猜你喜欢
    • 2019-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-27
    • 2015-08-28
    • 1970-01-01
    • 2012-03-03
    相关资源
    最近更新 更多