什么是僵局?

在编写multithreading应用程序时,遇到的最常见问题之一是死锁。

我对社区的问题是:

  1. 什么是僵局?

  2. 你如何检测他们?

  3. 你处理他们?

  4. 最后,你如何防止它们发生?

多个进程尝试同时访问同一资源时发生locking

一个过程失败了,必须等待另一个完成。

当等待进程仍然持有第一个需要的资源时,就会发生死锁

举个例子:

资源A和资源B被进程X和进程Y使用

  • X开始使用A.
  • X和Y尝试开始使用B
  • Y'胜利',并获得B第一
  • 现在Y需要使用A
  • A被Xlocking,等待Y.

避免死锁的最好方法是避免以这种方式交叉进程。 减less尽可能多地locking任何东西的需要。

在数据库中,避免在单个事务中对不同的表进行大量更改,避免触发器,并尽可能地切换到乐观/脏/非locking读取。

让我来解释一个真实的世界(实际上并不是真正的)犯罪电影僵局的例子。 想象一下,一个罪犯拥有一个人质,一个警察也拥有一个人质是罪犯的朋友。 在这种情况下,如果警察不让他的朋友放手,犯罪分子就不会放人质。 另外警察不会让罪犯的朋友放手,除非罪犯释放人质。 这是一个无止境的不可信赖的局面,因为双方都坚持相互的第一步。

刑事和警察的场景

在这里输入图像说明

简而言之,当两个线程需要两个不同的资源,并且每个资源都有另一个需要的资源locking时,就是一个死锁。

死锁的另一个高层次的解释:破碎的心

你和一个女孩约会,有一天吵架后,双方互相伤心欲绝,等待着我的抱歉和我错过了你的电话。 在这种情况下,只有当他们中的一个收到对方的电话,双方才想互相沟通。 因为每个人都不会开始沟通和等待处于被动状态,所以两个人都会等待对方开始沟通,最终陷入僵局。

死锁只有当你有两个或更多的锁可以在同一时间获得,并以不同的顺序抓住时才会发生。

避免陷入僵局的方法是:

  • 避免locking(如果可能的话),
  • 避免有多个锁
  • 始终以相同的顺序取锁。

要定义死锁,首先我要定义进程。

过程 正如我们所知,过程只不过是执行中的一个program

资源 执行程序进程需要一些资源。 资源类别可能包括内存,打印机,CPU,打开文件,磁带机,CD-ROM等。

死锁 死锁是两个或多个进程持有一些资源并试图获取更多资源的情况或条件,并且在完成执行之前不能释放资源。

死锁情况或情况

在这里输入图像说明

在上图中有两个进程P1P2 ,有两个资源R1R2

资源R1分配给进程P1 ,资源R2分配给进程p2 。 为了完成进程P1的执行,需要资源R2 ,所以P1请求R2 ,但R2已经分配给P2

以相同的方式处理P2完成其执行需求R1 ,但R1已经分配给P1

这两个进程不能释放他们的资源,直到他们完成执行。 所以两个都在等待另一个资源,他们将永远等待。 所以这是一个DEADLOCK条件。

为了发生僵局,四个条件必须是真实的。

  1. 相互排斥 – 每个资源目前只能分配给一个进程,或者可用。 (两个进程不能同时控制相同的资源或处于关键部分)。
  2. 保持并等待 – 当前持有资源的进程可以请求新的资源。
  3. 不抢占 – 一个进程持有资源后,不能被其他进程或内核带走。
  4. 循环等待 – 每个进程都在等待获取另一个进程持有的资源。

这些条件都满足上图。

你可以看看这个精彩的文章 ,在“ 僵局 ”一节下。 它在C#中,但其他平台的想法仍然是一样的。 我在这里引用以方便阅读

当两个线程每个都等待另一个线程占用的资源时发生死锁,因此两者都不能继续。 最简单的方法来解释这是两个锁:

 object locker1 = new object(); object locker2 = new object(); new Thread (() => { lock (locker1) { Thread.Sleep (1000); lock (locker2); // Deadlock } }).Start(); lock (locker2) { Thread.Sleep (1000); lock (locker1); // Deadlock } 

死锁是OS中多处理/多程序devise问题中的一个常见问题。 假设有两个进程P1,P2和两个全局可共享资源R1,R2,并且在临界区两个资源都需要被访问

最初,OS分配R1来处理P1和R2来处理P2。 由于两个进程同时运行,他们可能会开始执行他们的代码,但是当进程遇到关键部分时会出现问题。 所以进程R1将等待进程P2释放R2,反之亦然…所以他们将永远等待(死锁状态)。

一个小的类比..

你的母亲(OS),
你(P1)
你的兄弟(P2),
苹果(R1)
刀(R2),
关键部分(用刀切苹果)。

你的母亲一开始就把你的苹果和刀给你的兄弟。
他们都很高兴和玩(执行他们的代码)。
你们中的任何一个人都想在某个时候切断苹果(临界区)。
你不想把苹果给你的兄弟。
你的兄弟不想把刀给你。
所以你们俩都要等很久很久:)

当线程正在等待永远不会发生的事情时,会发生死锁。

通常,当一个线程正在等待前一个拥有者从未释放的互斥体或信号量时,就会发生这种情况。

当你遇到涉及两个线程和两个锁的情况时也经常发生:

 Thread 1 Thread 2 Lock1->Lock(); Lock2->Lock(); WaitForLock2(); WaitForLock1(); <-- Oops! 

您通常会检测它们,因为您期望发生的事情永远不会执行,或者应用程序完全挂起。

当有一个循环的线程或进程,每个持有一个locking的资源,并试图locking链中下一个元素所拥有的资源时,会发生死锁。 例如,两个线程分别持有锁A和锁B,并且都试图获取另一个锁。

当两个线程locking防止它们中的任何一个进行时,会发生死锁。 避免这些问题的最好方法是谨慎发展。 许多embedded式系统通过使用看门狗定时器(一旦定时器挂起一段时间就会复位系统的定时器)来保护它们。

死锁可以被正式地定义为:如果集合中的每个进程正在等待集合中只有另一个进程可能导致的事件,则一组进程被死锁。 因为所有的进程都在等待,所有的进程都不会引发任何可能唤醒其他任何成员的事件,并且所有进程都会一直等待下去。 下面是发生死锁时必须存在的四个条件。 如果其中一人不在场,则不会出现僵局。

相互排斥条件:每个资源当前分配给一个进程或可用。

保持和等待条件:当前持有资源的进程可以请求新的资源。

没有先占条件:先前授予的资源不能被强制从stream程中带走。 他们必须由持有他们的过程明确释放。

循环等待条件:必须有两个或多个进程的循环链,每个进程都在等待链中下一个成员持有的资源。 总的来说,有四个策略用于处理死锁。 它们是:完全忽略这个问题。 也许如果你忽略它,它会忽略你。 检测和恢复:让死锁发生,检测并采取行动。 通过谨慎的资源分配dynamic避免。 预防:通过结构性否定导致僵局的四个必要条件之一。

死锁是一个系统的状态,其中没有一个进程/线程能够执行一个动作。 正如其他人所提到的,死锁通常是每个进程/线程希望获得对已经被另一个(甚至相同)进程/线程locking的资源的锁的情况的结果。

有各种方法来find它们,并避免它们。 一个人在想很难和/或尝试很多东西。 然而,处理并行性是非常困难的,大多数人(如果不是全部的话)将不能完全避免问题。

如果你认真对待这些问题,一些更正式的方法可能会有用。 我所知道的最实用的方法是使用过程理论方法。 在这里,您使用某种stream程语言(例如CCS,CSP,ACP,mCRL2,LOTOS)为系统build模,并使用可用工具(模型)检查死锁(以及其他一些属性)。 要使用的工具集的示例是FDR,mCRL2,CADP和Uppaal。 一些勇敢的灵魂甚至可能通过纯粹的符号方法(定理certificate;寻找Owicki-Gries)来certificate自己的系统是自由的。

然而,这些forms化方法通常需要一些努力(例如学习过程理论的基础知识)。 但我想这只是这些问题很难解决的结果。

如果不同进程请求可用资源的数量较less,则会发生死锁。 这意味着,当可用资源的数量变得比用户所要求的less时,那么这个过程就处于等待状态。有时等待的时间增加了,没有机会检查资源缺乏的问题了。这种情况被称为僵局。 实际上,死锁是我们面临的一个主要问题,它只发生在多任务操作系统中。因为所有的资源只存在于当前正在运行的任务中,所以在单任务操作系统中不会出现死锁。

以上一些解释是很好的。 希望这也可能有用: https : //ora-data.blogspot.in/2017/04/deadlock-in-oracle.html

在数据库中,当一个会话(如ora)想要一个由另一个会话(例如数据)持有的资源时,该会话(数据)也需要一个由第一个会话(ora)持有的资源。 也可以有两个以上的会议,但想法也是一样的。 事实上,死锁阻止了一些交易继续工作。 例如:假设,ORA-DATA存放锁A并请求锁B而SKU存放锁B并请求锁A.

谢谢,

互斥实质上是一个锁,提供对共享资源的受保护的访问。 在Linux下,线程互斥量数据types是pthread_mutex_t。 在使用之前,初始化它。

要访问共享资源,您必须locking互斥锁。 如果已经在锁上的互斥锁,该调用将阻塞该线程,直到该互斥锁被解锁。 完成对共享资源的访问后,您必须解锁它们。

总的来说,有一些不成文的基本原则:

  • 在使用共享资源之前获取locking。

  • 尽可能短地握住锁。

  • 如果线程返回错误,则释放锁。