cppreference中对宽松排序的解释是否错误? [英] Is the explanation of relaxed ordering erroneous in cppreference?

查看:171
本文介绍了cppreference中对宽松排序的解释是否错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在cppreference.com上std::memory_order文档中,有轻松订购的示例:

In the documentation of std::memory_order on cppreference.com there is an example of relaxed ordering:

轻松订购

标记为memory_order_relaxed的原子操作不是同步操作;它们不会在并发内存访问之间强加顺序.它们仅保证原子性和修改顺序的一致性.

Relaxed ordering

Atomic operations tagged memory_order_relaxed are not synchronization operations; they do not impose an order among concurrent memory accesses. They only guarantee atomicity and modification order consistency.

例如,x和y最初为零,

For example, with x and y initially zero,

// 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,这是因为尽管A在线程1中位于B之前中,而C在线程2中在D之前在之前没有什么可以阻止D以y的修改顺序出现在A之前,以及B以x的修改顺序出现在C之前. D对y的副作用对于线程1中的负载A可见,而B对x的副作用对于线程2中的负载C可见.特别是,如果D在C in之前完成,则可能发生这种情况.线程2,由于编译器重新排序或在运行时.

is allowed to produce r1 == r2 == 42 because, although A is sequenced-before B within thread 1 and C is sequenced before D within thread 2, nothing prevents D from appearing before A in the modification order of y, and B from appearing before C in the modification order of x. The side-effect of D on y could be visible to the load A in thread 1 while the side effect of B on x could be visible to the load C in thread 2. In particular, this may occur if D is completed before C in thread 2, either due to compiler reordering or at runtime.

说在线程2中,C在D之前被排序".

it says "C is sequenced before D within thread 2".

根据序列前的定义,可以在 中找到评估顺序 ,如果A在B之前排序,那么A的评估将在B的评估开始之前完成.由于C在线程2中在D之前排在C之前,因此C必须在D开始之前完成,因此快照的最后一句的条件部分将永远无法满足.

According to the definition of sequenced-before, which can be found in Order of evaluation, if A is sequenced before B, then evaluation of A will be completed before evaluation of B begins. Since C is sequenced before D within thread 2, C must be completed before D begins, hence the condition part of the last sentence of the snapshot will never be satisfied.

推荐答案

我相信cppreference是正确的.我认为这可以归结为假设"规则 [intro.execution ]/1 .编译器仅应重现代码描述的程序的可观察行为.从执行这些评估的线程的角度来看,先序关系仅在评估之间建立

I believe cppreference is right. I think this boils down to the "as-if" rule [intro.execution]/1. Compilers are only bound to reproduce the observable behavior of the program described by your code. A sequenced-before relation is only established between evaluations from the perspective of the thread in which these evaluations are performed [intro.execution]/15. That means when two evaluations sequenced one after the other appear somewhere in some thread, the code actually running in that thread must behave as if whatever the first evaluation does did indeed affect whatever the second evaluation does. For example

int x = 0;
x = 42;
std::cout << x;

必须打印42.但是,编译器实际上不必在从该对象读取值42之前将值42存储到对象x中.可能还记得,要存储在x中的最后一个值是42,然后在将值42实际存储到x之前直接打印值42.实际上,如果x是局部变量,那么它也可能只跟踪该变量在任何时候最后分配的值,甚至从不创建对象或实际存储值42.线程没有办法告诉区别.行为总是好像有一个变量,而好像值42实际上存储在对象x 之前从该对象加载.但这并不意味着生成的机器代码必须实际存储和加载任何地方的任何内容.所需要做的就是,如果实际发生所有这些事情,则生成的机器代码的可观察行为与该行为是无法区分的.

must print 42. However, the compiler doesn't actually have to store the value 42 into an object x before reading the value back from that object to print it. It may as well remember that the last value to be stored in x was 42 and then simply print the value 42 directly before doing an actual store of the value 42 to x. In fact, if x is a local variable, it may as well just track what value that variable was last assigned at any point and never even create an object or actually store the value 42. There is no way for the thread to tell the difference. The behavior is always going to be as if there was a variable and as if the value 42 were actually stored in an object x before being loaded from that object. But that doesn't mean that the generated machine code has to actually store and load anything anywhere ever. All that is required is that the observable behavior of the generated machine code is indistinguishable from what the behavior would be if all these things were to actually happen.

如果我们看

r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D

然后是的,C在D之前被排序.但是从该线程单独看时,C所做的任何事情都不会影响D的结果.D所做的任何事情都不会改变C的结果.其他可能是由于另一个线程中发生某事的间接结果.但是,通过指定std::memory_order_relaxed,可以明确说明,另一个线程观察装载和存储的顺序是不相关的.由于没有其他线程可以观察到负载并以任何特定顺序进行存储,因此没有其他线程可以使C和D以一致的方式相互影响.因此,实际执行加载和存储的顺序是无关紧要的.因此,编译器可以自由地对其进行重新排序.而且,如该示例下的解释中所述,如果D的存储是在C的加载之前执行的,则r1 == r2 == 42的确可以出现…

then yes, C is sequenced before D. But when viewed from this thread in isolation, nothing that C does affects the outcome of D. And nothing that D does would change the result of C. The only way one could affect the other would be as an indirect consequence of something happening in another thread. However, by specifying std::memory_order_relaxed, you explicitly stated that the order in which the load and store are observed by another thread is irrelevant. Since no other thread can observe the load and store in any particular order, there is nothing another thread could do to make C and D affect each other in a consistent manner. Thus, the order in which the load and store are actually performed is irrelevant. Thus, the compiler is free to reorder them. And, as mentioned in the explanation underneath that example, if the store from D is performed before the load from C, then r1 == r2 == 42 can indeed come about…

这篇关于cppreference中对宽松排序的解释是否错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆