我们来为“死锁的四个必要条件”加一条

2020-07-26 09:13:39 +08:00
 gantleman

我们都知道多线程下必须要彻底解决死锁的问题。因为死锁是无法使用常规测试手段发现的,并且在项目运行中随机出现导致软件拒绝服务的一级 BUG 。

死锁问题是由 E. G. Coffman,M. J. Elphick,A. Shoshani 在 1971 年的论文“System Deadlocks”提出的。并且给出了知名的4种解决方式,被称为“死锁的四个必要条件”。

原文如下为: “

This deadlock situation has arisen only because all of the following general conditions were operative:

  1. Tasks claim exclusive control of the resources they require ("mutual exclusion" condition).
  2. Tasks hold resources already allocated to them while waiting for additional resources ("wait for" condition).
  3. Resources cannot be forcibly removed from the tasks holding them until the resources are used to completion ("no preemption" condition).
  4. A circular chain of tasks exists, such that each task holds one or more resources that are being requested by the next task in the chain ("circular wait" condition).”

互斥条件:一个资源每次只能被一个任务使用。 请求和保持条件:一个任务因为请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:任务已经获得的资源在没有使用完之前,不能强行剥夺。 循环等待条件:若干任务之间形成一种头尾相接的循环等待资源关系。

这4个必要条件做了一个隐含假设的条件,就是 task 是必须运行在不同的线程或进程中。 哪么这个隐含假设如果不成立,死锁也必然不会发生。所以4个必要条件中缺乏1个隐含的必要条件。 task 之间必须是并行才会发生死锁。

假设两个运行在不同线程或进程中的 task 会因为两个 resources 发生死锁。如果将两个 task 放在一个线程或进程中依次执行,哪么死锁也不会发生。

这就给了我一个新的启发,如果能发现两个 task 有潜在的死锁可能。把他们重新放回到一个线程中,就可以彻底解决软件死锁的问题。

关注项目  github.com/surparallel/pelagia  发现一些不同的并行计算的知识。

9988 次点击
所在节点    程序员
84 条回复
lance6716
2020-07-26 10:35:24 +08:00
@gantleman 有很多死锁检测和死锁避免算法,我觉得他们比起“合并任务减少并行度”更不影响效率,不知道你这个方法有什么优势

另外也不知道你这个方法如何合并任务,请给个具体例子。
比如任务 ABC 根据用户输入不同使用相同或者不同的资源,你这个方法是不是 ABC 按照最坏情况考虑,一定会串行处理
Akiyu
2020-07-26 10:51:51 +08:00
是的, 按照你说的, 的确可以规避死锁.
极端情况, 比如说只有两个线程执行两个任务, 当冲突的时候, 将两个任务放到一个线程中.
这当然可以规避死锁(都只有一个任务了!).

但显而易见有些缺陷和矛盾, 一是和本身多线程的想法有点冲突, 二是任务的粒度加大, 并发性进一步降低.
多线程是串行转并行, 为了提高效率. 但按你这么说, 反而并行转回串行(虽然是部分, 但的确有).
和本身引入多线程技术的想法冲突了. 而且, 将两个任务合并为一个任务, 这样任务的粒度就会变大. 并发性再次减少.
考虑一下这种情况: A 和 B 冲突, AB 合并(合并后, 该任务执行时间必定加长), 如果一种比较糟糕的情况出现了, 比如出现了 C 任务, 他和 AB 冲突了. 这种情况怎么办? 再次合并么?
719465553
2020-07-26 11:18:10 +08:00
什么脑回路,单进程,就不会死锁,这个意思?死锁的四个条件你认真理解了吗。如果能发现两个 task 有潜在的死锁可能。把他们重新放回到一个线程中,就可以彻底解决软件死锁的问题。同一个线程,也只是降低资源互斥的可能性,还有跨进程的问题
gantleman
2020-07-26 11:26:52 +08:00
@Akiyu 是的没错,打开了一个新世界的大门了吧!任务不光可以拆分还能合并。很多技术细节没办法在一个帖子说清楚的。以后想到什么慢慢说吧。
momocraft
2020-07-26 11:29:00 +08:00
第一次看了原文

有限的 cpu, 即导致线程不能同时前进的那个东西, 在原文被分类为资源.
liuminghao233
2020-07-26 12:18:57 +08:00
“多线程下必须要彻底解决死锁的问题”


总结一下:
A B 两个 task 在多线程下会出现死锁
怎么解决?
那就不用多线程。
jinliming2
2020-07-26 12:46:11 +08:00
我的理解,你自己加的这个“隐含假设的条件”不对,多线程并不是死锁的条件,单线程下依旧会出现死锁。
只不过实际开发在单线程条件下,因为通常不会出现资源访问冲突,所以单线程开发是不会加锁的,也就不会死锁。
但是,如果在单线程下使用了锁,那么如果资源获取、释放的顺序不对,也会产生死锁。也就是条件中的循环依赖,自己依赖自己。
最简单的例子就是,对一个资源进行加锁访问,在没有释放的时候,再对其进行加锁。这符合四个条件。

真实的情况可能会复杂一些,就是在多线程环境下,其中某一个线程出现了加锁、释放顺序不对的情况。
Jooooooooo
2020-07-26 13:34:28 +08:00
你这不是死锁问题

是定义问题
Jooooooooo
2020-07-26 13:35:55 +08:00
另外别人在一个 50 年前论文提出的相当著名的东西, 一直都不被人认为是错误的.

那么你认为他是错误并且可以证明, 那至少是值得重新写一篇论文而不是发 github
mind3x
2020-07-26 13:52:57 +08:00
恭喜你重新发明了 coroutine
kkbblzq
2020-07-26 14:23:21 +08:00
你以为解决问题的理论实际上并不是你的理论。。你这玩意都本质跟那些分段加锁其实没什么区别
Mohanson
2020-07-26 14:56:51 +08:00
死锁是代码逻辑有问题吧,我们应该修复逻辑 bug 而不是引入一个东西隐藏 bug
luozic
2020-07-26 15:55:28 +08:00
无并发,单线程跑?
leimao
2020-07-26 15:56:05 +08:00
我觉得 AsyncIO 基本就是这套路。
leimao
2020-07-26 15:56:48 +08:00
或者是结合 AysncIO 和 Thread 。但是这应该不是新的理念。
leimao
2020-07-26 16:00:27 +08:00
@Jooooooooo 看他意思也不是说前人是错的,他意思是在四个必要条件下,在得出一个推论出来的必要条件。但这个可能是 trivial 的 。
gantleman
2020-07-26 16:06:25 +08:00
@kkbblzq 今天我轻描淡写的说出来,简单的不可思议。但我不光在理论上尝试解决这个问题,还写了一万字的论文和 4 万多行代码的工程和测试用例。我不是万能的,如果在并行计算有类似论文和解决方案也可以推荐给我。都是成年人了,请尽量让自己的回复能够对别人有帮助。
leimao
2020-07-26 16:11:48 +08:00
@gantleman 可以贴一下论文地址,这样有这方面背景的人可以深入了解一下。
SingeeKing
2020-07-26 16:22:54 +08:00
「如果将两个 task 放在一个线程或进程中依次执行,哪么死锁也不会发生。」这明显忽略了不可重入锁啊……
gantleman
2020-07-26 18:33:06 +08:00
@SingeeKing
@jinliming2
在单线程里死锁就失去可不可测试随机出现的条件。单线程被阻塞无论黑盒还是白盒跑一遍就发现了。写出有随机性质的需要一定的经验和技巧。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/693150

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX