我知道线程2中的发布存储操作和线程1中的获取加载操作之间会发生同步关系,即使该加载操作不直接读取线程存储的值2,只要有一个"释放顺序"在发布商店操作和实际读取的商店之间只要:
但是,如果发布存储操作仍然存在,那么当实际读取的存储位于不同的线程中时,我没有看到任何原因导致无法进行同步的原因。发生在实际读取的商店之前。标准明确不允许这样做吗?如果是这样,那么标准是否有可能是不完整的,因为它是有意义的,所有现有硬件无论如何都会有这样的同步?
考虑以下示例,其中a,x和y是原子int用0初始化。
主题1:
k = y.load(memory_order_acquire);
x.store(1, memory_order_relaxed);
主题2:
m = x.load(memory_order_relaxed);
y.store(2, memory_order_release);
a.store(2, memory_order_release);
主题3:
n = a.load(memory_order_acquire);
y.store(3, memory_order_relaxed);
问题是,我们最终可能得到k = 3,m = 1和n = 2吗?
如果线程2中的商店与y之间没有释放序列,而线程3中的商店与y之间没有释放序列,那么在线程2中的释放存储与y之间没有同步 - 并且在线程中获取y的读取因此,线程2中的x的负载不必在线程1中存储到x之前发生,从而使得k,m和n的期望结果成为可能。
但是,如果是在线程2中存储到y和线程3中存储到y之间的释放序列那么是之间的同步 - 在线程2中释放存储到y并且在线程1中读取y,因此线程2中的x的加载需要发生 - 在线程1中存储到x之前,使得k,m和n的期望结果不可能。请注意,如果那里没有存储/加载,我们只是在线程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);
}
}}}
}
<{3>} 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);
}
}}}
}
然后没有一致的执行,即:
通过让节点f从节点e读取而f发生在节点e之前来打破因果关系。这里的主要区别是,现在有一个&#39; rs&#39;从节点g到h的边缘,这导致从节点g到节点d的同步(sw)边缘,因此在相同节点之间发生在(hb)边缘。