我正在阅读C标准中与多线程执行相关的一部分,并且对用于定义inter-thread happens before的Sequenced-before的定义非常困惑:
5.1.2.4/16
:
A
是inter-thread happens before B
,如果对于某个动作X
A
在X
之前,X
线程间在B
之前
最初我认为,如果在程序顺序中动作A
在动作B
之前,那么A
在B
之前排序,但请考虑下面这个简单的例子:
int read_write(int *a, int *b) {
*a = 10;
return *b;
}
,它编译为
read_write:
mov DWORD PTR [rdi], 10
mov eax, DWORD PTR [rsi]
ret
很明显,如果*a
和*b
是不相关的存储位置,那么根据Intel Manual Vol.3
:
-
store
可以与更高版本的load
一起重新排序到无关的内存位置
很明显,由于存储-缓冲转发,这种重新排序发生在硬件级别上,但标准明确规定,排序之前是关于如何准确完成判断的:
5.1.2.3/3
:
Sequenced before是一个不对称的、传递的、成对的关系 在由单个线程执行的计算之间,这会导致 这些评价中的部分顺序.给定任意两个评价A和 B,如果A在B之前排序,则A的执行应先于 B的执行.(相反,如果A在B之前排序,则B是 A后排序).如果A不是在B之前或之后排序,则A 而B是未排序的.
在本例中未指定.因此,这意味着存储*a = 10;
对于加载*b
是unsequenced.《旗帜报》在5.1.2.3/6
处指出
对对象的易失性访问严格按照 抽象机器的规则.
104使至少一个指针指向volatile
变量是否足够,或者int *a
和int *b
必须都是volatile
以确保先排序后运算?
我的意思是:
int read_write(volatile int *a, int *b) {
*a = 10;
//sequenced before since a points to volatile int
return *b;
}
和
int read_write(int *a, volatile int *b) {
*a = 10;
//sequenced before since b points to volatile int
return *b;
}
但是加volatile
并不会改变编译后的代码,这是比较令人困惑的.