为什么放松操作可以重新排序?程序顺序不是意味着发生之前吗?



在《C++并发在行动》一书中,当引入宽松排序时,作者说:

对不同变量的宽松操作可以自由重新排序,前提是它们服从绑定的关系之前发生的任何事情

但在cpprreference的这一页中,它给出了一个关于放松排序的示例

// Thread 1:
r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // B
// Thread 2:
r2 = x.load(std::memory_order_relaxed); // C 
y.store(42, std::memory_order_relaxed); // D

被允许产生r1==r2==42,因为尽管在线程1中A在B之前排序,而在线程2中C在D之前排序,但没有什么能阻止D以y的修改顺序出现在A之前,而B以x的修改顺序显示在C之前。D对y的副作用可以对线程1中的负载A可见,而B对x的副作用对线程2中的负载C可见。特别是,如果由于编译器重新排序或在运行时,D在线程2中先于C完成,则可能会发生这种情况。

我的问题是:在线程2中,C在D之前排序,因此C也发生在D之前,对吧?

操作D和C的重新排序是否与书中所说的相矛盾,即放松的操作可以重新排序,但必须在关系发生之前遵守?在什么情况下可以重新安排放松的操作?

这是一个常见的误解。的确,加载C发生在存储D之前。这是而不是说C必须在D之前执行,或者变得可见。

在一天结束的时候,先发生的关系或记忆模型的任何其他元素的唯一相关性是它"告诉"你你的程序将实际做什么(它的可观察行为(。这最终取决于负载返回的值。先发生后发生的关系以三种方式提供此类信息:

  • 链接页面上解释的一致性规则(写-写、读-写等(,

  • 告诉你是否有数据竞赛

  • 通过";可见副作用";规则(cppreference错误地陈述了这一规则:它用修改顺序来表达它,但它旨在应用于C++20中没有修改顺序的非原子变量。(

所有这些规则最终都是基于知道同一变量的两次读取或写入之间是否存在先发生后发生的关系

因此,由于C和D是对不同变量的访问,我们关心一个是否发生在之前,另一个的唯一原因是,我们是否可以将该事实作为更长推理链的一部分,最终推断出对同一变元的两次访问之间发生在之前(或"不发生在之前"(。在手头的计划中,我们没有办法做到这一点。C发生在D之前的语句是真的,但完全没有用,因此它不会以任何方式限制编译器/机器重新排序C和D的实际执行或可见方式。

在理解发生在之前、>之前排序的依赖关系等关系时,最好以实际的形式定义为指导,而不是以你对其名称似乎意味着什么的直觉为指导。这些名字只是名字。

最新更新