多线程执行和数据竞争(五)
对于对象M的值计算B,如果对象M上的副作用A可见,应满足下述条件:
① A在B之前发生。
② 对象M上不存在其它副作用X,使得A在X之前发生,并且X在B之前发生。
由评估B确定的非原子标量对象M的值应为可见副作用A存储的值。
由评估B确定的原子对象M的值应为修改M的某个副作用A存储的值,并且B不会发生在A之前。
对于下面的例子,假设x是全局变量,并且x的初始值为0。
//线程一
x = 5; |
//线程二
x = 3; |
//线程三 while(!x) ; x++; |
以下两种情况线程一中表达式x = 5的副作用对线程三中表达式x++的评估可见:
1、线程二中表达式x = 3的评估发生在线程一中表达式x = 5的评估之前。
2、线程二中表达式x = 3的评估发生在线程三中表达式x++的评估之后。
这个例子中线程一中表达式x = 5和线程二中表达式x = 3存在数据竞争。如果线程二中表达式x = 3的评估发生在线程一中表达式x = 5的评估和线程三中表达式x++的评估之间,线程一中表达式x = 5的副作用对线程三中表达式x++将不可见。
对于非原子对象M,如果哪种副作用是可见的存在歧义,则存在数据竞争,其行为是未定义的。
普通变量(ordinary variables)的操作没有明显重排。如果没有数据竞争,这实际上是无法检测到的;但有必要确保此处定义的数据竞争,以及对原子使用进行适当的限制,与简单交错(顺序一致)执行中的数据竞争相对应。
6、一致性
一致性(coherence)包括:写-写一致性(write-write coherence)、读-读一致性(read-read coherence)、读-写一致性(read-write coherence)、写-读一致性(write-read coherence)。
一致性要求(coherence requirement)有效地禁止了编译器对单个对象的原子操作重排,即使这两个操作都是松散的加载操作(relaxed loading operation)。这样使大多数硬件提供的“缓存一致性(cache coherence)”保证可有效地用于C原子操作。
原子对象加载所观察到的值取决于“之前发生(happens before)”关系,而“之前发生(happens before)”关系又取决于原子对象加载操作时所观察到的值。预期解读是:原子加载与观察到的修改之间存在某种关联,这种关联与适当选择的修改顺序(modification orders)以及“之前发生(happens before)”关系一起,满足了此处施加的约束条件。
如果程序的不同线程中包含两个相互冲突的操作,其中至少一个非原子操作,并且两者不存在“之前发生(happens before)”关系,则该程序的执行包含数据竞争。任何此类数据竞赛都会导致未定义行为。
如果程序正确使用简单互斥(mutex)和内存顺序一致(member_order_seq_cst)操作来防止数据竞争,并且不使用其它同步操作,其表现就像组成线程执行的操作简单地交错进行,对象的每次值计算都是交错中最后存储的值。这通常称为“顺序一致性(sequential consistency)”。“顺序一致性”仅适用于无数据竞争(data-race-free)程序,而无数据竞争程序无法遵守大多数不改变单线程程序语义的程序转换。事实上大多数单线程程序转换仍然是允许的,因为任何因这种转换而产生不同行为的程序,即使在应用这种转换之前,也必然存在未定义行为。
主要参考资料:
3、《C语言程序设计 现代方法》第2版•修订版 作者:K.N.King 出版社:人民邮电出版社
4、cppreference.com : memory_order
5、cppreference.com : order of evaluation
6、stackoverflow.com : What does the [[carries_dependency]] attribute mean?
7、《深入理解计算机系统》原书第3版 作者:Randal E. Bryant David R. O'Hallaron 出版社:机械工业出版社
8、sciencedirect.com : synchronization operation
9、open-std.org : weaken release sequences
10、sciencedirect.com : Synchronization Operation
11、lenovo.com : What is synchronization