是否可以从发布存储操作到另一个线程中的存储具有发布序列?

Car*_*ood 6 c++ concurrency multithreading memory-model stdatomic

我知道在线程2中的发布存储操作和线程1中的获取加载操作之间将发生同步关系,即使该加载操作不直接读取线程2存储的值,前提是存在发布存储操作与实际读取的存储之间的"释放顺序",只要:

  1. 实际读取的存储与发布存储操作位于同一个线程中.
  2. 在修改顺序中,在发布存储操作和实际正在读取的存储之间的其他线程中没有存储(尽管允许读取 - 修改 - 写入操作).

但是,我没有看到任何理由为什么在实际读取的存储位于不同的线程中时也不可能同步,前提是发布存储操作仍然发生 - 在存储之前实际上正在阅读.标准明确不允许这样做吗?如果是这样,那么标准是否可能不完整是因为它有意义并且所有现有硬件都会有这样的同步?

考虑以下示例,其中a,x和y是使用0初始化的原子int.

线程1:

k = y.load(memory_order_acquire);
x.store(1, memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)

线程2:

m = x.load(memory_order_relaxed);
y.store(2, memory_order_release);
a.store(2, memory_order_release);
Run Code Online (Sandbox Code Playgroud)

线程3:

n = a.load(memory_order_acquire);
y.store(3, memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)

问题是,有可能我们最终得到k = 3,m = 1和n = 2?

如果线程2中的存储到y和线程3中的存储到y之间没有释放序列,那么在线程2中的释放存储到y之间没有同步 - 并且在线程1中没有与y的获取读取,因此线程2中的x的负载不必在线程1中存储到x之前发生,使得k,m和n的期望结果成为可能.

但是,如果有商店至y在线程2和存储到y在螺纹3之间的释放顺序则一个进行同步-与释放存储到y在线程2和获取在线程1读出用Y之间因此,线程2中x的负载需要在线程1中存储到x之前发生,从而无法获得所需的k,m和n结果.请注意,如果a的存储/加载不在那里,我们只是在线程2的末尾做了值为3到y的松弛存储,那么情况就是如此(所以k = 3和m =永远不会发生1).

在这种情况下,值3到y的存储发生在线程3中,但是存在使用原子变量a的释放 - 获取同步; 因此,如果n = 2,那么在值2到y的释放存储和值3到y的松弛存储之间存在先发生关系.并不意味着存在一个释放顺序和结果,其中k = 3,M = 1且n = 2永远不会发生?

编辑

请注意,运行以下代码段:

int main()
{
  atomic_int a = 0;
  atomic_int x = 0;
  atomic_int y = 0;

  {{{
    {
      y.load(memory_order_acquire).readsvalue(3);
      x.store(1, memory_order_relaxed);
    }
  |||
    {
      x.load(memory_order_relaxed).readsvalue(1);
      y.store(2, memory_order_release);
      a.store(2, memory_order_release);
    }
  |||
    {
      a.load(memory_order_acquire).readsvalue(2);
      y.store(3, memory_order_relaxed);
    }
  }}}
}
Run Code Online (Sandbox Code Playgroud)

http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/上 导致1次一致的执行:

在此输入图像描述

原因是从节点g到节点j没有rs边缘(因此没有从j到d的sw/hb边缘).

比较一下,当我们将轻松的写入简单地放在线程2的末尾时:

int main()
{
  atomic_int a = 0;
  atomic_int x = 0;
  atomic_int y = 0;

  {{{
    {
      y.load(memory_order_acquire).readsvalue(3);
      x.store(1, memory_order_relaxed);
    }
  |||
    {
      x.load(memory_order_relaxed).readsvalue(1);
      y.store(2, memory_order_release);
      y.store(3, memory_order_relaxed);
    }
  }}}
}
Run Code Online (Sandbox Code Playgroud)

然后没有一致的执行,即:

在此输入图像描述

通过使节点f从节点e读取而f发生 - 在节点e之前 - 来打破因果关系.这里的主要区别在于,现在从节点g到h存在"rs"边缘,这导致从节点g到节点d的同步(sw)边缘,因此在相同节点之间发生前(hb)边缘.