13

假设我有一个线程Aatomic_int x = 0;使用x.store(1, std::memory_order_relaxed);. 如果没有任何其他同步方法,其他线程需要多长时间才能看到这个,使用x.load(std::memory_order_relaxed);x考虑到标准给出的 C/C++ 内存模型的当前定义,写入的值是否可能完全保持在线程本地?

我手头的实际情况是线程B经常读取 anatomic_bool以检查它是否必须退出;在某个时候,另一个线程对该布尔值写入true,然后在线程 B 上调用 join()。显然,我不介意在线程 B 甚至可以看到 atomic_bool 已设置之前调用 join(),我也不介意线程何时在我调用 join() 之前,B 已经看到了更改并退出了执行。但我想知道:memory_order_relaxed在双方都使用时,是否可以调用 join() 并“永远”阻塞,因为更改永远不会传播到线程 B?

编辑

我联系了 Mark Ba​​tty(数学验证并随后修复 C++ 内存模型要求的大脑)。最初是关于其他的(结果是 cppmem 和他的论文中的一个已知错误;所以幸运的是我没有完全自欺欺人,也借此机会向他询问了这个问题;他的回答是:

问:理论上这样的存储[memory_order_relaxed without (any following) release operation]是否永远不会到达其他线程?
马克:理论上,是的,但我认为没有观察到这一点。
问:换句话说,如果您希望另一个线程看到它,除非您将它们与一些释放操作(并在另一个线程上获取)结合起来,否则轻松存储是否毫无意义?
马克:几乎所有的用例都使用发布和获取,是的。

4

3 回答 3

9

这就是关于此事的所有标准,我相信:

[intro.multithread]/25实现应确保由原子或同步操作分配的最后一个值(按修改顺序)将在有限的时间段内对所有其他线程可见。

于 2017-05-03T04:51:53.890 回答
0

在实践中

如果没有任何其他同步方法,其他线程需要多长时间才能看到这个,使用 x.load(std::memory_order_relaxed);

没时间。这是一个正常的写入,它进入存储缓冲区,因此它将在比眨眼更短的时间内在 L1d 缓存中可用。但这只是运行汇编指令时。

指令可以由编译器重新排序,但没有一个合理的编译器会在任意长的循环中重新排序原子操作。

理论上

问:理论上这样的存储[memory_order_relaxed 没有(任何以下)释放操作]永远不会到达另一个线程吗?

马克:理论上,是的,

你应该问他如果添加“跟随释放围栏”会发生什么。或者用原子存储释放操作。

为什么不将这些重新排序并延迟很长时间?(只要它在实践中似乎是永恒的)

考虑到标准给出的 C/C++ 内存模型的当前定义,写入 x 的值是否可能完全保持在线程本地?

如果一个虚构的、特别是反常的实现想要延迟原子操作的可见性,为什么它只对宽松的操作这样做呢?它可以很好地完成所有原子操作。

或者从不运行一些线程。

或者运行一些线程太慢以至于你会相信它们没有运行。

于 2019-12-13T05:29:58.123 回答
-2

这就是标准在 29.3.12 中所说的:

实现应该使原子存储在合理的时间内对原子负载可见。

不能保证 astore将在另一个线程中可见,不能保证时间,并且与内存顺序没有正式关系。

当然,在每个常规架构上 astore 都会变得可见,但在不支持缓存一致性的罕见平台上,它可能永远不会对 a 可见load
在这种情况下,您必须进行原子读取-修改-写入操作以获取修改顺序中的最新值。

于 2017-05-03T05:09:12.093 回答