多线程执行和数据竞争(一)
一、相关术语
数据竞争(data race): 两个线程同时访问一个相同的内存位置,并且至少一个线程修改访问的内存位置,则会发生冲突,这称为数据竞争;如果没有线程修改访问的内存位置,则不会发生冲突。
int number = 0; struct timespec interval = {0,20}; /*新线程中执行的函数。*/ int func(void *arg) { thrd_sleep(&interval, 0); number = 5; thrd_exit(0); } /*主线程。*/ int main(void) { thrd_t threadId; /*创建新线程。*/ if(thrd_create(&threadId, func, NULL) != thrd_success) exit(EXIT_FAILURE); thrd_sleep(&interval, 0); printf("%d\n", number); //A /*连接新线程。*/ thrd_join(threadId, NULL); return 0; }
主线程读取变量number值,新线程修改变量number值,两者产生数据竞争。在位置A,如果新线程修改变量number值在主线程读取变量number值之前完成,则输出5;反之,则输出0。
同步(synchronization): 在计算机领域同步是指协调多个进程或者线程的活动和状态的过程。在多线程或者多进程环境中,同步对于确保数据一致性和防止数据竞争至关重要。
同步操作(synchronization operation): 同步操作是指协调线程间信息共享,以确保通过管理并行进程中多个进程的进度来高效执行指令。
atomic_int M = 0; /*线程一。*/ ... //其它操作。 atomic_thread_fence(memory_order_release); //A atomic_store_explicit(&M, 10, memory_order_relaxed); //X /*线程二。*/ while(!atomic_load_explicit(&M, memory_order_relaxed)) //Y ; atomic_thread_fence(memory_order_acquire); //B ... //其它操作。
释放栅栏A与获取栅栏B同步,A之前的所有写操作B之后可见。
栅栏(fence): 栅栏是无关联内存位置的同步操作。
修改顺序(modification order): 对于特定原子对象,其所有修改都是按照某种特定的总序进行,这种顺序称为原子对象的修改顺序。
写-写一致性(write-write coherence): 如果修改原子对象M的操作A发生在修改原子对象M的操作B之前,则在原子对象M的修改顺序中A应先于B。
读-读一致性(read-read coherence): 如果原子对象M的值计算A发生在原子对象M的值计算B之前,并且A从M上的副作用X获取值,那么B计算的值要么是X存储的值,要么是M上的副作用Y存储的值,并且在M的修改顺序中Y在X之后。
读-写一致性(read-write coherence): 如果原子对象M的值计算A发生在对原子对象M的操作B之前,A将从M上的副作用X中获取值,在M的修改顺序中X应先于B。
写-读一致性(write-read coherence): 如果原子对象M上的副作用X发生在原子对象M的值计算B之前,则评估B将从X或者从原子对象M的修改顺序中X之后的副作用Y中获取值。
二、多线程执行和数据竞争
未定义__STDC_NO_THREADS__的宿主环境中一个程序可以同时执行多个线程,整个程序的执行包括其所有线程的执行(执行通常可视为所有线程的交错。)。独立环境中一个程序是否存在多个执行线程将由实现定义。
在特定时刻,线程T可见的对象值可能是该对象的初始值、线程T存储在该对象的值或者其它线程存储在该对象的值。
|
|
注:使用Pelles C编译。
变量data是全局变量,其值对主线程可见;在位置A,data值是其初始值;在位置B,data值是新线程对其修改后的值;在位置C,data值是主线程对其修改后的值。