首页 > 编程知识 正文

预防死锁和避免死锁的方法,死锁避免方法

时间:2023-05-04 02:23:37 阅读:157517 作者:174

一、死锁的定义

多线程和多进程提高了系统资源的利用率,提高了系统的处理能力。 但是,同时执行带来了新的问题——死锁。 死锁是指由多个线程竞争资源导致的僵局(相互等待),如果没有外力的作用,这些进程就无法前进。

通过几个例子说明死锁现象。

首先来看看生活的一个例子。 两个人一起吃饭,但只有一根筷子。 两个人轮流吃(必须同时拿两根筷子才能吃)。 一次,一个人拿着左边的筷子,一个人拿着右边的筷子,两个人都同时占有一个资源,等待另一个资源。 此时,甲方正在等待乙方吃完,释放占有的筷子。 同样,乙方也在等待甲方吃完,释放占有的筷子。 这样的话,就会陷入死亡循环,谁也不能继续吃饭。 请参阅。 请参阅。

二、死锁产生的原因

http://www.Sina.com/http://www.Sina.com /

运行过程中(如磁带机、打印机等)可能会因为争夺资源而陷入困境,因为系统通常无法获取资源,而且其数量不足以满足多个进程的运行需求。 只有不可剥夺资源的竞争才可能发生死锁,而可剥夺资源的竞争不会发生死锁。

http://www.Sina.com/http://www.Sina.com /

在进程运行期间,请求和释放资源的顺序不正确会导致死锁。 例如,并发进程P1、P2分别保存资源R1、R2,但进程P1申请资源R2,进程P2在申请资源R1时,两者都被所需的资源占用而屏蔽。

如果弄错信号量的使用方法,也会导致死锁。 因为进程之间正在等待来自彼此的消息,所以结果是无法在这些进程之间前进。 例如,进程a等待来自进程b的消息,进程b等待来自进程a的消息。 可知进程a和b并不是在竞争同一资源,而是等待对方的资源而发生死锁。 ) )

http://www.Sina.com/http://www.Sina.com /

要发生死锁,必须同时满足以下四个条件,否则不会发生死锁:

3358 www.Sina.com/http://www.Sina.com /进程必须独占控制分配的资源(如打印机)。 这意味着,在一段时间内,某个资源只被一个进程占用。 如果此时另一个进程请求资源,则请求进程只能等待。

3358 www.Sina.com/http://www.Sina.com /进程获得的资源在未使用之前不得被其他进程强制剥夺。 也就是说,获得该资源的进程自身只能释放。 只能积极释放。

3358 www.Sina.com/http://www.Sina.com /进程已经保留了至少一项资源,但提出了新的资源要求。 该资源已经被其他进程占用,此时请求进程被屏蔽,但对自己获得的资源却置之不理。

存在3358 www.Sina.com/http://www.Sina.com /进程资源的循环等待链,链中的每个进程获得的资源被同时请求链中的下一个进程。 即,存在处于等待状态的进程的集合{Pl、P2、Pn},其中Pi等等待资源被p(I1 )占用) I=0、1、n-1 )、pn等等待资源为P0 如图2-15所示。

直观上,循环等待条件看起来和死锁的定义一样,但其实不然。 死锁定义要求配置等待环路所要求的条件更严格,Pi等待的资源必须由p(I1 )来满足,但循环等待条件没有这种限制。 例如,系统有两台输出设备,P0占一个

台,PK占有另一台,且K不属于集合{0, 1, ..., n}。Pn等待一台输出设备,它可以从P0获得,也可能从PK获得。因此,虽然Pn、P0和其他 一些进程形成了循环等待圈,但PK不在tzdzs,若PK释放了输出设备,则可打破循环等待, 如图2-16所示。因此循环等待只是死锁的必要条件。)

资源分配图含圈而系统又不一定有死锁的原因是同类资源数大于1。但若系统中每类资源都只有一个资源,则资源分配图含圈就变成了系统出现死锁的充分必要条件。

三、如何避免死锁

在有些情况下死锁是可以避免的。三种用于避免死锁的技术:

1.加锁顺序(线程按照一定的顺序加锁)

2.加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

3.死锁检测

 

(1)加锁顺序:

当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。

如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。

(2)加锁时限:

另外一个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行(加锁超时后可以先继续运行干点其它事情,再回头来重复之前加锁的逻辑)。

(3)死锁检测:

死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。

当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。(例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1))。

当然,死锁一般要比两个线程互相持有对方的锁这种情况要复杂的多。线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着。这是它就知道发生了死锁。

下面是一幅关于四个线程(A,B,C和D)之间锁占有和请求的关系图。像这样的数据结构就可以被用来检测死锁。

那么当检测出死锁时,这些线程该做些什么呢?

(1)一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁(原因同超时类似,不能从根本上减轻竞争)。

 

 

 

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。