在多线程编程中,死锁恢复是否可能?

4

该进程有10个线程,且所有10个线程都处于死锁状态(假设它们都在等待Mutex变量)。

如何使进程(线程)从死锁状态中解脱出来呢?是否有一种方法可以杀死优先级较低的线程?(在多进程的情况下,当所有进程都处于死锁状态时,我们可以杀死优先级较低的进程。)

我们可以将死锁进程附加到调试器上,并为Mutex变量分配适当的值吗?(假设所有线程都在等待一个名为MUT的Mutex变量,但它的值为0,我们可以通过调试器将MUT的值分配为1吗?)


2
是的,这是可能的 - 设计和实现一切与线程相关的内容时要非常小心并祈祷 ;) - user395760
2
可能是有可能的,但它非常依赖于实现。对于您似乎在询问的一般情况,最具体的答案是“可能”。能否提供更多细节?(此外,您的标题正在询问死锁“预防”,而正文则在询问“解决” - 比喻来说,“预防车祸”是一个非常不同的问题,与“从车祸中恢复”不同;您想要哪一个?) - Piskvor left the building
2个回答

5
如果应用程序中的每个线程都在等待其他线程,而且没有设置超时,那么你可能会遇到麻烦。你可能能够在调试器或其他工具中运行应用程序,但是锁通常是为了某个原因而获取的——手动强制一个互斥量归属于没有合法获取它的线程可能会导致一些大问题(之前拥有它的线程仍然会尝试释放它,如果互斥量被意外地夺走,则结果可能是不可预测的。可能会引起意外异常,也可能会在使用时解锁互斥量)。总之,这违背了互斥量的整个目的,所以你只是掩盖了一个更大的问题。
有两种常见的解决方案:
- 不要让线程永远等待,设置超时。在像Java这样通过synchronizedlock块将互斥量嵌入语言中的语言中,这稍微有点难,但几乎总是可以做到的。如果等待锁超时,请释放您拥有的所有锁/互斥量,并稍后再试。 - 更好的解决方案,但可能更加复杂,是找出为什么所有东西都在争夺资源并消除争用。如果必须锁定,请保持一致。但是,如果有10个线程在单个互斥量上阻塞,那可能是一个线索,表明您的操作被块状化得很糟糕(即:您的线程在尝试获取锁之前做太多或太少的工作),或者存在不必要的锁定。除非必须锁定,否则不要锁定。使用专门设计为“无锁定”但仍提供线程安全性的集合和算法可以省略一些同步。

但是,在释放所有锁/互斥量后,系统如何达到相同的状态(无死锁)? - Karthik Balaguru
@KarthikBalaguru:没有一些帮助,它不会。任何已经进行的更改都已经完成,并且通常不会自动恢复。您必须使用两阶段/事务性机制(暂时记录更改,并仅在成功完成后一次性提交所有更改),或者记住足够的信息以便在释放同步它们的锁之前撤消更改。 - cHao

4
添加另一个答案,因为我不同意之前cHao提出的解决方案——分析是正确的。
首先,我为什么不同意提供的两个解决方案:
减少争用
争用不会导致死锁。它只会导致性能下降。死锁意味着没有任何性能。因此,减少争用并不能解决死锁问题。
在mutex上设置超时。
mutex保护资源,线程锁定mutex是因为它需要该资源。使用超时,您将无法获取该资源,您的线程失败。它解决了死锁问题吗?只有当失败的线程释放了阻塞其他线程的另一个资源时才能解决。
但在这种情况下,有一个更好的解决方案。Mutex应该具有部分排序。如果至少有一个线程可以同时mutex A和B,则应确定是先获取A还是B,然后坚持这一点。这必须是一个传递性顺序:如果您在锁定A之前锁定B,然后在锁定C之前锁定B,那么显然您必须在锁定C之前锁定A。
这是解决死锁的完美解决方案。回顾超时示例:它仅在等待A超时的线程随后释放其对B的锁定以释放正在等待B的另一个线程时才起作用。在最简单的情况下,另一个线程直接锁定A。因此,mutex A和B没有正确排序。您应该始终首先锁定A或B。
超时案例也可能是循环顺序问题的结果;一个线程锁定A然后B,另一个线程锁定B然后C,第三个线程锁定C然后A,在每个线程拥有一个锁时发生死锁。解决方案再次相同;排序锁。
换句话说,mutex锁定顺序可以由有向图描述。如果一个线程在B之前锁定A,则从A到B有一条弧。如果有向图是循环的,则会出现死锁,然后该周期的弧是死锁的线程。
这个理论可能有点复杂,但是可以找到一些简单的见解。例如,从图论中,我们知道树是无环图。因此,“叶子mutex”(始终最后被锁定的mutex)和“根mutex”(始终最先被锁定的mutex)都不会导致死锁。排除了叶子mutex,因为没有任何线程会持有它们进行阻塞,而排除了根mutex,因为持有它们的线程将能够及时锁定所有后续mutex。

我知道线程的ID。我该如何终止该线程?如果我终止了该线程,它会中止整个进程(其中有多个线程)吗? - siva
@siva:杀死一个线程并不能解决死锁问题。你的线程是相互依赖的(它们共享资源,否则就不会死锁)。如果你杀死其中一个,最好把所有线程都杀掉。其他线程无法可靠地继续执行。现在,理论上来说,死锁线程组可能是孤立的,你的进程可以从失去所有这些线程中恢复过来。实际上,你应该记录错误并终止整个进程,而不仅仅是一个线程。 - MSalters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接