一.互斥量(mutex)
多个线程同时访问共享数据时可能会冲突。比如某个线程把某个全局变量增加1,这个操作在某平台需要三条指令完成:
(1)从内存读变量值到寄存器;
(2)寄存器的值加1;
(3)将寄存器的值写会内存。
假设两个线程在多处理器平台上同时执行这三条指令,则可能导致最后变量只加了1次,而不是2次。
程序如下:
1 #include<stdio.h>
2 #include<pthread.h>
3 #define MAX 5000
4 static int g_count=0;
5 void *thread_run(void *arg)
6 {
7 int val=0;
8 int i=0;
9 for(i=0;i<MAX;++i)
10 {
11 val=g_count;
12 printf("pthread id is:%u,count is:%d\n",\
13 pthread_self(),g_count);
14 g_count=val+1;
15 }
16 return NULL;
17 }
18 int main()
19 {
20 pthread_t tid1,tid2;
21 pthread_create(&tid1,NULL,thread_run,NULL);
22 pthread_create(&tid2,NULL,thread_run,NULL);
23 pthread_join(tid1,NULL);
24 pthread_join(tid2,NULL);
25 printf("count final val is:%d\n",g_count);
26 return 0;
27 }
运行结果:
创建两个线程,各自把g_count增加5000次,正常情况下g_count的最后结果应该是10000,但是事实上每次运行该程序的结果都不一样,大概是5000多次。
对于多线程的程序,访问冲突的问题很普遍,解决的办法是引入互斥锁,获得锁的线程可以完成“读——修改——写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待而不能访问共享数据,这样“读——修改——写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断。
mutex的相关操作如下:
返回值:成功返回0,失败返回错误号。
pthread_mutex_init函数初始化的mutex可以用pthread_mutex_destroy销毁。如果mutex变量是静态分配的(全局变量或者static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且参数arr为NULL。mutex的加锁和解锁操作如下:
返回值:成功返回0,失败返回错误号。
一个线程可以调用pthread_mutex_lock获得mutex,如果这时另一个线程已经调用了pthread_mutex_lock获得该mutex,则当前进程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放mutex,当前线程被唤醒,才能获得该mutex并继续执行。
如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。
程序实现如下:
1 #include<stdio.h>
2 #include<pthread.h>
3 #define MAX 5000
4 pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
5 static int g_count=0;
6 void *thread_run(void *arg)
7 {
8 int val=0;
9 int i=0;
10 for(i=0;i<MAX;++i)
11 {
12 pthread_mutex_lock(&lock);
13 val=g_count;
14 printf("pthread id is:%u,count is:%d\n",\
15 pthread_self(),g_count);
16 g_count=val+1;
17 pthread_mutex_unlock(&lock);
18 }
19 return NULL;
20 }
21 int main()
22 {
23 pthread_t tid1,tid2;
24 pthread_create(&tid1,NULL,thread_run,NULL);
25 pthread_create(&tid2,NULL,thread_run,NULL);
26 pthread_join(tid1,NULL);
27 pthread_join(tid2,NULL);
28 printf("count final val is:%d\n",g_count);
29 return 0;
30 }
运行结果如下: