什么是死锁?
一般情况下,如果同一个线程先后两次调⽤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调⽤,以免死锁。
产生死锁的原因主要是:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
如何预防死锁的出现 :
(1) 资源一次性分配:(破坏请求和保持条件)
(2) 可剥夺资源:即当某进程新的资源未满足时,释放已占有的资源(破坏不可剥夺条件)
(3)资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
避免死锁
最具有代表性的避免死锁算法是银行家算法:
设进程cusneed提出请求REQUEST [i],则银行家算法按如下规则进行判断。
(1)如果REQUEST [cusneed]
(2)如果REQUEST [cusneed] [i]<= AVAILABLE[i],则转(3);否则,等待。
(3)系统试探分配资源,修改相关数据:
AVAILABLE[i]-=REQUEST[cusneed][i];
ALLOCATION[cusneed][i]+=REQUEST[cusneed][i];
NEED[cusneed][i]-=REQUEST[cusneed][i];
(4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。