单个进程/线程会导致死锁吗?

时间:2020-05-13 07:02:47

标签: multithreading process operating-system locks

我正在从Galvin中读取死锁的概念,并且我怀疑单个进程/线程是否可以陷入死锁...? Coz的定义(或者事实上,在Galvin中整个“死锁”一章中)似乎都没有在谈论如果系统中只有一个进程/线程。 (请PLZ告诉我是否有任何遗漏……阅读本书时……如果是的话,诚挚的道歉……为我先前的发言,但我在任何地方的章节中都找不到。)

在描述死锁情况时,高尔文书中的每个地方都使用“其他”一词... 所以我觉得我的问题的答案是“否”,单个进程/线程永远不会陷入僵局。 (我也是:我觉得在某些情况下一个进程可能导致无限期的等待,我可以称之为死锁吗?)

要了解为什么我将死锁和无限期等待合并到一张图片中的动机,请阅读以下方案 (还请告诉我,假设无限期等待与死锁并不相同,我是否正确……我可能是错的。 考虑一个场景: 有一个线程(t)和一个锁(l)。 锁定性质不允许进入。 (意味着线程持有锁l时,它无法在释放它之前再次获取它...我只能在互联网上找到它作为定义。) (另一个条件: 如果线程无法获取锁,则它将阻塞自身,直到它可用为止。是的,这很明显,但这是造成混乱的原因。请阅读下面的plz以了解情况。) 现在声称t说获得了一个锁l,然后执行它,同时又需要相同的锁。(可能是因为它必须执行一些递归函数调用……就像在BFS / DFS中一样……或者是类似的东西..) 因此很明显,它必须在获取该锁之前离开该锁...但是由于进程无法再次获取相同的锁,因此它必须等待或只是被阻塞直到它可用为止... 现在重要的一点是...它将被阻塞,直到它..自身释放锁..现在我的问题是这种情况是否会导致死锁... (是的,它可以再次释放..然后重新获取...但是我的问题与这种情况无关。) 所以我的问题是这种情况是否会导致死锁...(就像线程在等待自己...) ->就像在最坏的情况下一样。 (此外,每当进程/线程进入阻塞状态/等待状态时,它都会保持锁/资源。.plz也在这里阐明...) (我希望我在说的是明确的...如果不发表评论,并告诉我会尽力澄清...是的,这一点在我要提出疑问的地方非常微妙)

这种情况实际上是一个考试问题。其答案是:是的,具有单个锁的单个线程会导致死锁->我觉得这与死锁定义冲突。 我知道我提出了很多疑问,但主要问题是相同的。

以下是我所怀疑的重点摘要:

-单进程/处于死锁状态?

-无限等待vs死锁

-进程/线程是否有必要释放锁..何时进入阻塞/等待状态?

(首先感谢您是否到此为止...因为这确实是一个长期的疑问。.我这样做是为了明确我的观点...如果不发表评论。.再次..)

3 个答案:

答案 0 :(得分:2)

单进程/陷入僵局?

是的,正如您提到的,如果线程中的函数持有锁并递归调用自身,则可能导致死锁。如果锁已实现为不阻塞,那么当已经持有的线程请求该锁时,我们当然不会出现死锁。

不确定等待vs死锁

不,这些不是一回事。如果所有线程都无法取得进展,则会发生死锁。如果一个线程(持有锁)在无限期阻塞/延迟其他线程的同时能够取得进展,那不是死锁。另一个有趣的概念是Livelocks-线程没有取得进展,但似乎是“活跃的” /活动的。

进程/线程是否有必要释放锁..何时进入阻塞/等待状态?

例如,假设某个线程持有一个锁以保护内存页面免于其他线程访问,然后开始从磁盘读取该内存页面。该I / O很可能会挂起线程,直到从磁盘完成传输为止。这是保持锁定和暂停的合法使用。在许多情况下,线程在持有锁的同时可能会阻塞。

答案 1 :(得分:1)

编写死锁的过程很容易。尝试两次获取信号量...但是,这样的代码每次都会死锁,因此,这并不是真正的问题,因为这样的错误也无法逃脱即使是最不专心的测试人员的注意。

死锁问题

通常,当多线程程序有可能发生死锁时,死锁是一个问题,但只有在事件的时间安排很不幸时才会这样做(例如,两个线程必须在某些时间达到其执行的特定点才能执行死锁)。发生死锁)。这使得通过测试进行检测变得非常困难。在这样的程序中避免使用它需要开发人员正确设计其代码。

对于使用共享内存和信号灯来保护访问的系统,在非平凡的设计中很难通过分析证明设计没有死锁。

通信顺序过程

有一些方法可以断定软件设计没有死锁。查找通信顺序过程。这是Tony Hoare在1970年代开发的过程计算,在1980年代很流行(体现在Transputer上),最近在Rust和Go等语言中卷土重来。

一个CSP设计可以完成的过程计算-数学-使设计人员能够分析性地证明他们的设计没有死锁问题。这实际上已经完成; Wikipedia页面引用了一种通过这种方式证明的电子商务系统。

CSP在代码面上的实用性是您具有通过“通道”相互通信的独立进程/线程,发送/接收是同步的。也就是说,当两个线程交换消息时,发送方将被阻止,直到接收方收到一条消息为止。这是Rust和Go都重新引入的内容,我忍不住对“发现” CSP的人们笑了一下(自从第一次想到以来已有40多年了)。

这样做的好处是,不必通过数学来证明设计没有死锁。如果您以CSP风格编写代码,运行它,并且它不会死锁,那么它永远不会死锁。

CSP也非常适合实时系统;未能满足实时性能要求并不能通过网络缓冲区的延迟等来掩饰。

这与早期的相关Actor模型形成鲜明对比。在这种情况下,发送者和接收者是不同步的。发送者发送并恢复执行,他们的消息位于某个缓冲区或队列中的某个地方或其他地方,最终传递给接收者。 Actor模型系统可能会死锁(和发生死锁),并且有一天可能会发生这种情况,即网络基础结构变得比平时更加​​繁忙。

现代技术

可以观察到的是,Internet上使用的大多数技术都遵循(一种或另一种)Actor模型; ip / tcp不提供发送方和接收方之间的同步。这很公平;要在相隔半个星球的进程之间实现同步,需要跨网络进行大量流量的往返传输。异步对于大规模跨行星网络的有效性能更好。

这意味着,尽管Go和Rust之类的语言在单台计算机上的一个进程中重新引入了CSP的思想,但是并没有很多跨计算机网络的CSP实现(即,TCP / IP之上的CSP )。我认为Erlang就是这样做的。

ZeroMQ非常接近完成此操作(晶须遗漏-最小输出缓冲区大小为1 ...)。

作为CSP的忠实支持者,我偶尔在顶级TCP / IP上实现了自定义CSP库,事实证明它非常有用。

答案 2 :(得分:0)

关注您的帖子并不容易,但让我举一些例子

如果线程正在请求一个已经拥有的应用程序锁,那么一个好的锁管理器应该抛出一个异常。 您需要在应用程序中包含记录您拥有锁的代码,以便可以在异常或正常终止时释放它。

锁可以是您说“获取唯一锁”的锁对象。

两个线程可以使用ECB / Mutex上的等待和发送进行通信。 因此task1发布了互斥锁1,然后等待着互斥锁2

任务2正在等待互斥锁1,醒来,做了一些工作,发布互斥锁1,等待mutext2

任务1醒来

如果没有运行任务2,则仅执行任务1,然后让任务1等待永远不会发布的互斥量。可以将其视为只有一个线程的死锁。


如果任务1具有锁1,正在等待锁2,任务2具有锁2,并且正在等待锁1,您还可以获得“致命的拥抱”。


某些系统在获得lock2之前需要具有锁层次结构,然后才需要锁1。

所以您的代码是

get lock1
get lock2 
release lock1
...
release lock2

这避免了致命的拥抱。

如果您要锁定数据库中的行,则可能会遇到以下情况:更新需要对数据,页面,行等进行多次锁定,并在此处出现死锁-因此一项任务需要许多锁定。