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

Can a single Process / Thread ever cause deadlock?

我正在阅读 Galvin 的死锁概念,我怀疑单个进程/线程是否会陷入死锁...? 因为定义(或者事实上,Galvin 中的整个死锁章节)似乎并没有在谈论如果系统中只有一个进程/线程会怎样…… (请告诉我是否遗漏了任何一点......在阅读它时......如果是的话,真诚的道歉......对于我之前的陈述,但我根本无法在任何地方找到它......)

Galvin Book 在描述死锁场景时每处都使用 "Other" 进程这个词... 所以我觉得我的问题的答案是否定的,单个进程/线程永远不会陷入死锁。 (我也是:我觉得在某些情况下单个进程可能会导致无限期等待..我可以将其称为死锁吗..?)

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

那个场景实际上是一道考试题..其答案是:是的,单线程和单锁会导致死锁-->我觉得这与死锁定义冲突..) 我知道我提出了很多疑问,但主要问题是一样的。

下面是我疑惑的要点总结:

-单进程/有死锁?

-无限期等待与死锁

- process/thread 是否有必要释放锁..何时进入 blocked/waiting 状态?

(首先感谢你做到了这一点......因为这真的是一个长期的疑问......我这样做只是为了阐明我的观点......如果不发表评论......我会的再做一次..)

要跟上你的步伐并不容易post,但让我举一些例子

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

锁可以是你所说的锁对象"get unique lock"。

两个线程可以在 ECB/Mutex 上使用等待和 post 进行通信。 所以 task1 posts mutex1 并等待 mutex2

任务 2 正在等待 mutex 1,唤醒,做一些工作,posts mutex1,等待 mutext2

任务 1 唤醒

如果您没有任务 2 运行,只有任务 1,那么您会得到任务 1,等待永远不会 posted 的互斥锁。这可以认为是只有一个线程的死锁。


你还可以得到"a deadly embrace"其中任务1有锁1,正在等待锁2,任务2有锁2,正在等待锁1。


有些系统有一个锁层次结构,在你获得锁 2 之前你需要锁 1。

所以你的代码是

get lock1
get lock2 
release lock1
...
release lock2

这避免了致命的拥抱。

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

single process/ there's in deadlock?

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

indefinite wait vs deadlock

不,这些不是一回事。如果 none 个线程能够取得进展,则会发生死锁。如果一个线程(持有锁)能够在 blocking/delaying 其他线程无限期地取得进展,那不是死锁。另一个有趣的概念是活锁——线程没有取得进展但似乎是 "alive"/active。

is it necessary for a process/thread to release locks..when goes in blocked/waiting state?

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

编写一个会死锁的进程很容易。尝试使用信号量两次...但是,这样的代码每次都会死锁,所以这并不是真正的问题,因为即使是最粗心的测试人员也不会逃过这样的错误的注意。

死锁问题

通常,当多线程程序有可能发生死锁时,死锁就是一个问题,但只有在事件发生的时间不合适时才会发生死锁(例如,两个线程必须在特定时间到达特定的执行点发生死锁的顺序)。这使得通过测试更难检测到。在这样的程序中避免它需要开发人员正确设计他们的代码。

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

通信顺序进程

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

可以对 CSP 设计进行的过程计算 - 数学 - 允许设计人员通过分析证明他们的设计没有死锁问题。这在实践中已经完成;维基百科页面引用了一个以这种方式证明的电子商务系统。

CSP 在代码面上的实用性是您有独立的进程/线程通过 "channels" 相互通信,发送/接收是同步的。也就是说,当两个线程交换消息时,发送方被阻塞,直到接收方收到消息。这就是 Rust 和 Go 都重新引入的东西,我不禁对人们笑了笑 "discovering" CSP(从第一次想到它已经 40 多年了)。

这样做的美妙之处在于,不必通过计算来证明设计没有死锁。如果您以 CSP 风格编写代码,运行 它不会死锁,那么它永远不会死锁。

CSP 也非常适合实时系统;未能满足实时性能要求不会被网络缓冲区中的延迟等所掩盖。

这与早期的相关 Actor 模型形成鲜明对比。在这种情况下,发送者和接收者是不同步的;发件人发送并恢复执行,他们的消息位于某个缓冲区或某个地方或其他地方的队列中,并最终到达接收者。 Actor 模型系统可能会死锁(和活锁),这可能会在网络基础设施比平时稍微繁忙一点的那一天发生。

现代科技

可以观察到的是,互联网上使用的大多数技术都遵循(以某种方式)Actor 模型; ip / tcp 不提供发送方和接收方之间的同步。这很公平;要实现相距半个行星的进程之间的同步,需要大量的网络流量来回传输。异步更适合大规模、跨行星网络的高效性能。

这意味着,虽然像 Go 和 Rust 这样的语言在单台计算机上的进程中重新引入了 CSP 的概念,但跨计算机网络的 CSP 实现并不多(即 CSP 在 TCP/IP).我认为 Erlang 可以做到这一点。

ZeroMQ 非常非常接近于做到这一点(差点错过 - 它的最小输出缓冲区大小为 1 ...)。

作为 CSP 的粉丝,我偶尔会在上面实现自定义 CSP 库 TCP/IP,事实证明它非常有用。