一、 mutex互斥量
同步:就是对资源的访问有序。互斥:就是任一时刻来说只有一个在执行;但是对于多线程的程序来说,访问冲突的问题是很普遍的,解决的办法是引入互斥锁(Mutex,MutualExclusive Lock),获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有获得锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。
互斥锁用pthread_mutex_t类型的变量表示。用pthread_mutex_init初始化,用hread_destory()销毁。成功返回0,失败返回错误号。。如果Mutex变量是静态分配的(全局变量 或static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且attr参数为NULL
一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。意思就是说如果有一个线程对mutex上了锁,没有开锁,另外一个线程想获得mutex,就得挂机等待,直到上锁的线程开了锁释放开mutex之后,该线程被唤醒,才能获得mutex.
如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<pthread.h> 4 static int g_count=0; 5 void * addWrite(void * arg) 6 { 7 int count=0; 8 int value=0; 9 while(count++ <5000) 10 { 11 value=g_count; 12 printf("g_count is %d\n",g_count); 13 g_count=value+1; 14 } 15 } 16 int main() 17 { 18 pthread_t id1; 19 pthread_t id2; 20 int ret=pthread_create(&id1,NULL,addWrite,NULL); 21 int res=pthread_create(&id2,NULL,addWrite,NULL); 22 pthread_join(id1,NULL); 23 pthread_join(id2,NULL);
我们创建两个线程,各自把g_count增加5000次,正常情况下最后counter应该等于10000,但事实上每次运行该程序的结果都不一样,有时候数到5000多,有时候数到6000多但是加上锁之后(在)第五行加上pthread_mutex_init 如下图
结果:
加上锁之后,就输出10000
二、lock和unlock的实现原理
为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。如下图的伪代码所示。unlock中的释放锁操作同样只用一条指令实现,以保证它的原子性。
三、死锁
·一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此 就永远处于挂起等待状态了,这叫做死锁(Deadlock)。另一种典型的死锁情形是这样:线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态了。
死锁形成的条件
①、互斥条件:一个资源每次只能被一个线程使用。
②、请求与保持条件:一个进程因请求资源而被阻塞时,对已获得的资源保持不放。
③、不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
④循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
如果涉及到更多的线程和更多的锁,则更容易引起死锁问题。写程序时应该尽量避免同时获得多个锁,如果一定有必要这么做,则有一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁。比如一个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么 所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确定一个先后顺序比较困难,则应该尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock调用,以免死锁。