死锁及其应对方案

Posted Jqivin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了死锁及其应对方案相关的知识,希望对你有一定的参考价值。


一、死锁是什么?

1.死锁

有一组线程,每个线程都在等待某一个资源,但所需要的资源又是别的线程所持有的资源。则我们称这组线程发生了死锁。在死锁的状态下,没有线程可以释放资源,获取资源。线程都保持着自己的资源不释放,但是又得不到另外想要的资源,从而陷入了死锁的僵局。

2.例子

假如线程A获得x锁,线程B获得y锁,这时候线程A想要获得y锁,线程B想要获得x锁就造成了死锁。
在这里插入图片描述
在这里插入图片描述
图2-1

3.死锁的四个必要条件

①资源有限和独占:资源有限以至于不能满足所有线程所需的资源,并且资源是不能共享的。
②持有等待:当线程想要获取新的资源时,别的线程在使用,线程等待的时候不会释放原有的资源。
③不能抢占:如果可以对资源发生抢占,就不会发生死锁了。
④循环等待:在发生死锁时,必然存在一个进程–资源的环形链。
在这里插入图片描述

二、死锁的应对

允许死锁发生(不理睬,死锁发生后想办法解决)
不允许死锁发生(周全考虑,通过死锁的必要条件消除)

1.不予理睬

采取无为而治,因为死锁防止的代价太高,防止死锁比重启100次代价还要高,不如直接重启。

2.死锁的检测与修复

(1)检测死锁
①有向图:效率低下。
②矩阵:建立资源分配矩阵,资源等待矩阵。然后维持两个矢量:系统资源总量矢量,系统当前可用资源矢量。资源等待矩阵 - 系统当前可用资源矢量,如果每一行都为负数,那么就发生了死锁。
在这里插入图片描述在这里插入图片描述
(2)死锁的恢复
①抢占资源:代价是被抢占的线程可能不能正常运行。
②抢占资源并把线程“杀掉”。
③上翻:将整个系统翻转到以前的状态。需要对系统做定期的记录,很麻烦。
评价:行不通,两个步骤都有较大的困难,第一步减出来的结果都为负数的时候也不一定会发生死锁,因为可能之后会有线程异常退出,另外,如果是多用户系统的话,线程数非常多,矩阵也会很大,更新这个矩阵会很困难。最严重的是,检测死锁的进程自己可能发生死锁。

3.死锁的动态避免

先发制人,进行每次资源分配的时候,都要进行仔细的计算,确保该资源请求批准后系统不会进入死锁或者潜在死锁的状态(尚未发生死锁,但是之后可能会发生死锁 – 不安全状态)。如果分配该资源,系统将会进入不安全的状态,那么就不要分配。银行家算法就是这个方案。

安全状态:从该状态开始,我们能够找到一种资源分配的方法和顺序,使所有的进程都能够得到其所需要的资源,从而避免死锁。
不安全状态:从该状态开始,无论我们怎样,都找不到一种资源分配的方法和顺序,来满足所有的进程的资源需求。
例子
下面这个就是安全的状态:先给B分配资源,B执行结束之后再给C分配资源,C结束之后再给A分配资源。
在这里插入图片描述
下面这个就是不安全状态:B执行完毕之后,还有4个可用资源,既不能满足A也不能满足C,所以系统将进入死锁状态。
在这里插入图片描述
评价:①如果进程过多,系统资源种类过多,这种计算将变得十分的复杂。
②得到一个进程的最大资源需求 很困难。只能估算,不能准确的得到,但是估算又会造成我们在每一步计算的时候产生误差,造成结果的不准确。

4.死锁的静态防止

前三种的方法都不尽人意,该策略的中心思想就是清除死锁发生的土壤–死锁发生的四个必要条件。如果消除死锁发生的必要条件中的任何一个,都可以达到避免死锁的目的。
(1)消除资源独占条件
有两个方法:一个是把资源无限增加(显然行不通),另外一个是把资源变成共享(这种发放不适合所有的资源,有的可以共享,有的不可以共享)
(2)消除请求和保持条件
①一个进程一次请求所有的资源,而不是到需要下一个资源的时候再去请求下一个。 这种方法的缺点就是以此获得所有的资源,刚开始并不使用,造成浪费。
在这里插入图片描述
②另一个方法是,还像之前一样请求资源,当请求的资源被拒接之后,线程释放所有的资源。(也会造成浪费)
多线程中使用std::lock()可以绑定多个互斥量资源。
比如说,我们使用两个锁,上面的例子图2-1就造成了死锁。我们可以使用std::lock()解决。
在这里插入图片描述
为了方便,我们也可以使用lock_guard进行管理:
adopt_lock参数:std::adopt_lock是个结构体对象,起一个标记作用.作用就是表示这个互斥量已经lock(),不需要在locker1对象的构建过程中进行mtx1.lock();
在这里插入图片描述

(3)消除非抢占条件
允许资源的抢占,比如CPU和内存资源,但是有些资源是不能被抢占的,比如锁的资源。
(4)消除循环等待条件
死锁的最后一个必要条件是循环等待。而循环等待的原因是因为进程请求资源的顺序是随机的,即一个进程可以先请求资源A再请求资源B,也可以先请求资源B再请求资源A。这样,如果两个进程按照不同的顺序请求A、B两个资源,死锁就有可能发生。但如果我们规定对A、B两个资源的使用必须按昭先A后B的顺序诸求.则死锁就不能发生。比如说,一个进程想使用1,3资源,必须严格按照先请求1,再请求3的顺序进行,当一个进程使用3的时候,他也在使用1,所以其他进程就会等待,直到该进程把资源使用完毕释放。如果没有进程需要资源3,那么需要资源2的进程可以正常执行。
在这里插入图片描述

总结

在这里插入图片描述

以上是关于死锁及其应对方案的主要内容,如果未能解决你的问题,请参考以下文章

Java中各种死锁详细讲述及其解决方案(图文并茂,浅显易懂)

Java中各种死锁详细讲述及其解决方案(图文并茂,浅显易懂)

flink standalone 部署模式且不能使用 hdfs 场景下的各种问题及其应对方案

并发进阶常见的死锁类型

死锁产生的条件及其预防

死锁产生的条件及其预防