【经典结构】死锁

死锁

1.概念

一个很通俗易懂的例子:假设有红蓝两把钥匙开红蓝两个门,两个人分别执行六条指令,最后要能够都把两扇门打开。 注意红蓝钥匙都各只有一把,也就是说两个人共享这对钥匙。

比如下面这幅图的解法:

image

两个人同时执行,A能够顺利把门打开,因为它正好在第3步的时候能拿到B归还后的红钥匙,但是对B就不一样了,因为A没有及时归还蓝钥匙,在最后才想起来还,那B在第四步的时候想要拿到蓝钥匙,需要等A在第6步把蓝钥匙还了才行,也就是得等着A完事以后B才能完事,所以两个人完成时间并不相同(也就是两个线程A先结束,B再结束),但终究还是都完成了。
那再看下面这个解法:

image

两个人A和B在第三步的时候,A想要获取红钥匙,等着B归还红钥匙;B想要获取蓝钥匙,等着A归还蓝钥匙,那这两个人永远在这卡着,进行不下去了,这就是死锁

问:什么是死锁?

死锁就是多个线程在运行的时候互相抢夺资源,造成了一种僵持的阻塞状态。比如线程AB分别握着锁a和锁b,但是这时候两个线程又都想获得对方的锁,又谁都不松手,这时候就形成了僵局,成了阻塞状态。

2.条件

上面想开两扇门其实有很多种解法,那什么时候会出现死锁,比如同样的问题解法1就不会死锁,解法2就会死锁。一般来说,死锁有四个必要条件:

问:死锁产生的条件?

  • 互斥条件:某资源某一时刻只能被一个线程占用。--只有一对钥匙;
  • 请求与保持条件:当一个线程请求新的资源而阻塞后,对已经获得的资源保持不放。 --A拿着红钥匙又想要蓝钥匙,还不归还红钥匙;
  • 不剥夺条件:一个线程拿到资源后不能被剥夺,只能由自己释放。 --人除非自己主动归还钥匙,不然就一直占着;
  • 环路等待条件:在发生死锁时,必然存在着线程之间头尾相连循环等待资源的关系;比如线程P0等待着P1的资源,P1等待着P2的资源,...,Pn等待着P0的资源。 --拿着红钥匙的A在等蓝钥匙,拿着蓝钥匙的B在等红钥匙;

上面是产生死锁的四个必要条件,那如果想要避免,只需破坏其中一个就可以。

问:如何避免死锁?

  • 破坏互斥条件:这个条件没办法进行破坏,因为我们用锁的目的就是想让它们互斥。
  • 破坏请求与保持条件:只要有一个资源得不到分配,那也不给这个线程分配其他资源。 --一个人拿到一把钥匙后,只有还了才能去请求下一把;
  • 破坏不剥夺条件:当某线程拿着资源,但是得不到其他资源,那就释放自己的资源。 --比如可以设置A拿着钥匙等了十分钟还是执行不了下一步,那就自动归还自己的钥匙;
  • 破坏环路等待条件:每类资源都申请一个编号,每个线程按顺序申请资源,按相反顺序释放资源。 --上面产生死锁一定是A和B都先拿了不同的钥匙,比如可以规定任何线程都只能先拿红钥匙再拿蓝钥匙。

问:在实际中采用什么方法解决死锁?

  1. 银行家算法;
  2. 顺序加锁:确保线程按照相同的顺序获得锁(上面提到的4);
  3. 限时加锁:线程在尝试获取锁的时候加一个超时时间,超过这个时间就放弃该请求,并把自己获得的锁释放(上面提到的3);

问:银行家算法是什么,具体流程?

一句话概括:当一个进程申请使用资源的时候,银行家算法通过先试探性的分配给该进程资源,然后通过安全性算法判断分配后的系统是否处于安全状态。

四种结构

  • 最大需求资源数量Max(例如Max[j]=4,表示当前进程i对资源j的最大需求量为4);
  • 已分配给进程的资源Allocation(例如Allocation[j]=4,表示当前进程i已经分配的资源j数量为4);
  • 还需要的资源数量Need(例如Need[j]=4,表示当前进程i还需要资源j的数量为4);Need[j]=Max[j]-Allocation[j]
  • 空闲资源数量Available(例如Available[j]=4,表示当前资源j的空闲数量为4);

过程

  • 1.假设进程P1申请资源,银行家算法先试探性的分配给他(当然先看看当前资源池中的资源数量够不够)
    • if申请的资源数量<=Available(操作系统(银行家))所有的资源,那就执行2;
    • if申请的资源数量>Available-->不安全;
  • 2.判断分配给P1后剩余的资源,能不能使进程队列中的某个进程执行完毕;
    • if有进程可执行完毕,那就执行3;
    • if没有进程可执行完毕,那系统处于不安全状态(即此时没有一个进程能够完成并释放资源,随时间推移,系统终将处于死锁);
  • 3.经过2判断后有进程可执行完毕,则假设回收已经分配给它的资源(剩余资源数量增加),并把这个进程标记为可完成,然后接着判断队列中的其他进程;
  • 4.如果所有进程都可执行完毕,那系统就处于安全状态,并根据刚才的顺序生成安全序列(例如{P0,P3,P2,P1}表示将申请后的资源work先分配给P0->回收P0(原work+进程P0已分配的Allocation=新work)->分配给P3->回收P3->...

注意

  • 安全状态:如果存在一个由系统中所有进程构成的安全序列,那系统安全状态,安全状态一定没有死锁;
  • 不安全状态:不存在一个安全序列。不安全状态不一定死锁;

例子

比如当前出现下述资源分配:

image

上文一共有4种资源,例如P0 1号资源已经分配的为0;3号资源分配的为3;   

问:该状态是否安全

根据上面的过程可以列出下面的表:

image

work指的是资源池里可以利用的资源,根据上面的Available,发现其能满足P0的need,然后求出回收P0的新work,接着发现能满足P3,...,就这样不断进行;最后能找到一个序列{P0,P3,P4,P1,P2},所以系统是安全的。

问:if 进程P2提出请求Request(1,1,2,2),系统能分配给它吗

检查步骤:

  • 1.Request2(1,1,2,2)<=Need2(2,3,4,5)
  • 2.Request2(1,2,2,2)<=Available(1,6,2,2)
  • 3.两者都满足,那就先假设为P2分配资源,然后修改Available,Allocation,Need2;
    Available(0,4,0,0)
    Allocation2(2,5,7,6)
    Need2(1,1,3,4)
  • 4.这时候再进行检查,Available(0,4,0,0) 谁也满足不了了,系统进入不安全状态,所以不能分配给P2资源。

相关连接

通俗的死锁
银行家算法

posted @ 2021-12-03 22:34  Curryxin  阅读(526)  评论(0编辑  收藏  举报
Live2D