关于 acquire/release 语义不能与其他线程交换

On acquire/release semantics not being commutative to other threads

gcc wiki here 提供了有关内存排序约束的示例。

在下面的示例中,wiki 断言,如果使用的内存顺序是 acquire/release,那么 thead-2 的断言保证成功,而 thread-3 的断言可能会失败。

 -Thread 1-       -Thread 2-                   -Thread 3-
 y.store (20);    if (x.load() == 10) {        if (y.load() == 10)
 x.store (10);      assert (y.load() == 20)      assert (x.load() == 10)
                    y.store (10)
                  }

我不知道这怎么可能。我的理解是,由于 Thread-2y.store(10)Thread-3y.load() 同步,并且由于 y 可以是 10 当且仅当 x10,我说Thread-3中的assert应该成功。维基不同意。有人可以解释为什么吗?

and since y can be 10 if and only if x is 10,

这是不正确的部分。

acquire/release 对在释放存储操作和获取加载操作之间工作,后者读取 release-stored 的值。

在线程 1 中,我们有一个释放存储到 x。在线程 2 中,我们有一个来自 x 的获取负载。如果线程2读取了线程1存储的值,那么这两个线程就是acquire/release的关系。这意味着在释放存储之前写入线程 1 中其他对象的任何值在线程 2 获取加载后都是可见的。

在线程 2 中,我们有一个释放存储到 y。在线程 3 中,我们有一个来自 y 的获取负载。如果线程3读取了线程2存储的值,那么这两个线程就是acquire/release的关系。这意味着在释放存储之前写入线程 2 中其他对象的任何值在线程 3 获取加载后都是可见的。

但请注意我所说的:“在线程 2 中写入其他对象 的任何值”。

x不是线程2写的;它是由线程1编写的。线程3与线程1没有任何关系。

纯 acquire/release 不可传递。如果您需要对内存的可传递访问,那么您需要顺序一致性。事实上,这就是 seq_cst 的目的:确保在所有 acquire/release 之间保持一致,这些 acquire/release 相互依赖。

请注意,这主要是关于缓存的。一个纯粹的 acquire/release 操作可能只会刷新某些缓存,特别是如果编译器可以清楚地看到一个线程在一个版本中提供了哪些内存。