C++ 中的内存模型:顺序一致性和原子性

Memory Model in C++ : sequential consistency and atomicity

我对 C++11 中的内存模型有一些疑问。

https://www.think-cell.com/en/career/talks/pdf/think-cell_talk_memorymodel.pdf 29.幻灯片上写着

The C++ memory model guarantees sequential consistency

但是,在我以前的帖子中,我了解到 C++ 内存具有弱内存模型——编译器可以根据需要进行重新排序——他必须满足 as if 规则。

C++ 内存模型保证顺序一致性如果您使用具有适当内存顺序的原子操作来保证顺序一致性。如果您只是使用普通的非原子操作,或松散的原子操作,并且没有互斥锁,则无法保证顺序一致性。

如果无法观察到行为差异,编译器可以自由地重新排序操作,这是假设规则。因此,例如,如果重新排序顺序一致的原子将产生不同的可观察结果,那么它不符合假设规则。如果它不会产生不同的可观察结果,则允许重新排序。

我想我通过阅读前面的幻灯片了解了这张幻灯片的内容:

slide 12: sequential consistency [Leslie Lamport, 1979]
the result of any execution is the same as-if

  1. the operations of all threads are executed in some sequential order
  2. the operations of each thread appear in this sequence in the order specified by their program

slide14: sequential consistency for data-race-free programs
SC-DRF:

  • We take care our program does not contain data races
  • The system guarantees sequentially consistent execution

所以在幻灯片 29 上,作者说 一旦你使用 std::atomic 避免了数据争用 UB,程序 运行 就像一切都按程序顺序发生一样.(如果你所有的std::atomic操作都使用默认的memory_order_seq_cst)。

这是查看 C++ 的弱(对于非原子对象)内存模型的有趣方式。这看起来像是一套不错的幻灯片。 SC 原子操作是严格有序的,有点像非原子操作的单向屏障。(对于宽松的原子操作,如果有的话)。

请注意,无数据争用意味着您不能在任意时间查看非原子变量,只有当您确定没有其他线程正在写入它们时。 (通常通过与获取加载的同步关系来查看作者完成的释放存储或互斥体。)无数据争用部分是这里的关键;如果您不小心,很容易出现数据争用 UB。当编译为真正的 CPUs 的 asm 时,这意味着非原子访问可以正常工作,而 atomic<T> seq_cst 访问需要阻塞编译时间和 运行-time重新排序。 https://preshing.com/20120625/memory-ordering-at-compile-time/


第二部分:请不要养成一次问两个截然不同的问题的习惯。

这个“CPU 是怎么做到的?”问题更适合作为您稍后问题的一部分:

我已经写好了大部分答案,我会把它放在那里。

为具有未定义行为的语言定义语义的唯一可能方法是描述顺序执行。因此,所有程序的行为都与顺序执行相同,或者没有程序具有定义的行为。

你在 C/C++ 中同时拥有两者的想法是骗局。 程序必须按顺序运行才能使语义有意义。