1.死锁是什么
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
2.死锁产生的条件和原因
四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
两个原因:
(1)竞争系统资源
(2)进程的推进顺序不当
3.死锁具体产生情况
3.1、在A、B方法中相互等待unlock而导致死锁
mutex; //代表一个全局互斥对象
void A()
{
mutex.lock();
//这里操作共享数据
B(); //这里调用B方法
mutex.unlock();
return;
}
void B()
{
mutex.lock();
//这里操作共享数据
mutex.unlock();
return;
}
3.2、没有调用unlock,导致另一个线程再调用A方法就出现死锁
mutex; //代表一个全局互斥对象
void A()
{
mutex.lock();
//这里操作共享数据
if(.....)
{
return;
}
mutex.unlock();
return;
}
4.死锁的解决
对待死锁的策略主要有:
4.1死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
1:加锁顺序
2:加锁时限
4.2 死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
我们可以把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程操作系统请求分配资源相当于用户向银行家贷款。
为保证资金的安全,银行家规定:
(1) 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
(2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量;
(3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但能使顾客在有限的时间里得到贷款;
(4) 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金.
操作系统按照银行家制定的规则为进程分配资源,当进程首次申请资源时,要测试该进程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。当进程在执行中继续申请资源时,先测试该进程本次申请的资源数是否超过了该资源所剩余的总量。若超过则拒绝分配资源,若能满足则按当前的申请量分配资源,否则也要推迟分配。
4.3 死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。
先OO分析、建模,再看具体问题的方案。
这里面涉及的对象:线程、锁
关系:
(1)占有:线程——锁 的一对多关系:一个线程可以占有多个锁,一个锁只能被一个线程占有
(2)等待:线程——锁的多对一对多关系:一个线程一个时刻只能阻塞在一个锁上,一个锁可以被多个线程等待
(3)间接等待关系 线程——线程 的多对多关系,一个线程A等待的锁被另一个线程B拥有,则线程A对线程B有等待关系
输入模型:线程列表,锁列表,每个线程占有的锁列表,每个线程等待的锁列表
第一步:构建Wait For Graph,这是一个典型的二分图,两种类型的结点(锁,和线程),并且只有这两种不同类型的结点之间才有边。当然也可以以锁为中介,转换成线程之间的等待关系,构建一幅有向图(wait for graph)。转换依据就是:一个线程A等待的锁被另一个线程B拥有,则线程A对线程B有等待关系。
第二步:识别有向图中是否有环,这是个图论基本问题。
4.4 死锁解除:
剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。