c++ - 发布顺序的概念在实践中有用吗?

标签 c++ multithreading thread-safety memory-model stdatomic

C++ 原子语义仅保证由执行释放写入(简单或读取-修改-写入)操作的最后一个线程执行的内存操作的可见性(通过先发生关系)。

考虑

int x, y;
atomic<int> a;

线程 1:

x = 1;
a.store(1,memory_order_release);

线程 2:

y = 2;
if (a.load(memory_order_relaxed) == 1))
  a.store(2,memory_order_release);

那么 a == 2 的观察意味着线程 2 操作的可见性 (y == 2) 而不是线程 1(甚至无法读取 x )。

据我所知,多线程的实际实现使用栅栏概念(有时还使用释放存储),但不使用高级 C++ 概念发生前或释放序列;我看不出这些概念映射到了哪些真实的硬件细节。

a 中的 2 的值全局可见时,真正的实现如何不保证线程 1 内存操作的可见性?

也就是说,release-sequence的定义有什么好处吗?为什么发布顺序不会扩展到修改顺序中的每个后续修改?

特别考虑傻线程 3:

if (a.load(memory_order_relaxed) == 2))
  a.store(2,memory_order_relaxed);

silly-thread 3 能否在任何真实硬件上抑制任何可见性保证?换句话说,如果值 2 是全局可见的,如何使其再次全局可见会破坏任何排序?

我对真实多处理的心智模型不正确吗?部分可见的值可以在某些 CPU 上但注意到另一个吗?

(当然,我假设一个非疯狂的语义用于轻松的写入,因为回到过去的写入使 C++ 的语言语义绝对荒谬,不像 Java 这样的安全语言总是具有有限的语义。没有真正的实现可以有疯狂的,非-因果放松语义。)

最佳答案

让我们先回答你的问题:

Why wouldn't the release-sequence extend to every subsequent modification in the modification order?

因为如果是这样,我们将失去一些潜在的优化。例如,考虑线程:

x = 1;                            // #1
a.store(1,memory_order_relaxed);  // #2

根据当前规则,编译器能够重新排序#1 和#2。然而,在释放序列的扩展之后,编译器不允许对这两行重新排序,因为另一个线程像你的线程 2 可能会引入一个以#2 为首的释放序列,并以一个释放操作结尾,因此有可能有些读- 另一个线程中的获取操作将与#2 同步。


您举了一个具体的例子,并声称所有的实现都会产生一个特定的结果,而语言规则并不能保证这个结果。这不是问题,因为语言规则旨在处理所有情况,而不仅仅是您的具体示例。当然可以改进语言规则,以便它可以保证您的特定示例的预期结果,但这不是一项微不足道的工作。至少,正如我们上面所说,简单地扩展发布顺序的定义并不是一个可以接受的解决方案。

关于c++ - 发布顺序的概念在实践中有用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56031747/

相关文章:

行为类似于 __COUNTER__ 宏的 C++ 结构

c++ - C++中 "Class<>"是什么意思?

c++ - C++ 中的字符串和整数连接

c# - .NET 4.0 System.Collections.Concurrent 集合在 .NET 3.0 SynchronizedCollection 的功能中添加了什么?

c# - 如何以线程安全的方式更新控件

c++ - 在一个文件设备中 boost 接收器和源

java - 每个客户端一个线程。可行吗?

c++ - std::atomic 加载和存储都需要吗?

c++ - pthread C++ 中的 Obj-C performSelector OnThread

java - 是否可以在一种方法中获取锁并从另一种方法中释放它?