当前位置: C语言 -- 基础 -- 多线程执行和数据竞争

多线程执行和数据竞争(一)


数据竞争(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之前,并且AM上的副作用X获取值,那么B计算的值要么是X存储的值,要么是M上的副作用Y存储的值,并且在M的修改顺序中YX之后。


读-写一致性(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存储在该对象的值或者其它线程存储在该对象的值。

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
/*线程可见的对象值范例*/

#ifdef __STDC_NO_THREADS__
#error "Implementation does not support multi-threads."
#endif

#include <stdio.h>
#include <stdlib.h>
#include <threads.h>

int data = 5;

/*新线程中执行的函数。*/
int func(void *ptr)
{
    data += 10;
    
    thrd_exit(0);
}

int main(void)
{
    thrd_t threadId;
    
    printf("Position A data = %d\n", data);   //A
    
    /*创建新线程。*/
    if(thrd_create(&threadId, func, NULL) != thrd_success)
        exit(EXIT_FAILURE);

    /*连接新线程。*/
    thrd_join(threadId, NULL);
    
    printf("Position B data = %d\n", data);   //B

    data += 20;

    printf("Position C data = %d\n", data);   //C
    
    return 0;
}

注:使用Pelles C编译。

变量data是全局变量,其值对主线程可见;在位置Adata值是其初始值;在位置Bdata值是新线程对其修改后的值;在位置Cdata值是主线程对其修改后的值。