11

我有 2 个进程(A、B)共享相同的互斥锁(使用 WaitForSingleObject / ReleaseMutex 调用)。一切正常,但是当进程 A 崩溃时,进程 B 正在愉快地嗡嗡作响。当我重新启动进程 A 时,出现了死锁。

更深入的调查显示,在进程 A 崩溃后,进程 B 可以成功调用 ReleaseMutex() 两次。

我的解释:进程 A 崩溃后,互斥锁仍然被锁定,但互斥锁的所有权很容易转移到进程 B(这是一个错误)。这就是为什么它愉快地嗡嗡作响,调用 WaitForSingleObject(得到 WAIT_OBJECT_0 作为回报)和 ReleaseMutex(得到 TRUE 作为回报)。

是否可以使用类似于 Mutex 的命名同步原语,以使进程 A 中的崩溃将释放互斥锁?

一种解决方案是使用 SEH 并捕获崩溃并释放互斥锁,但我真的希望 Windows 有一个健壮的原语,不会像进程崩溃那样死锁。

4

2 回答 2

29

关于互斥锁在 Windows 上的工作方式,您必须在这里做出一些基本假设:

  • 互斥锁是一个引用计数的操作系统对象。它不会消失,直到互斥体上的最后一个句柄关闭
  • 进程终止时未关闭的任何句柄都会被操作系统关闭,从而减少引用计数
  • 互斥体是可重入的,在同一线程上的互斥体上调用 WaitForSingleObject 成功,需要与相同数量的 ReleaseMutex 调用进行平衡
  • 当拥有它的线程在没有调用 ReleaseMutex 的情况下终止时,拥有的互斥锁将被放弃。在此状态下对互斥体调用 WaitForSingleObject 会生成 WAIT_ABANDONED 错误返回码
  • 它绝不是操作系统中的错误。

所以你可以根据你的观察得出结论。当 A 崩溃时,互斥体没有任何反应,B 仍然有一个句柄。B 可以注意到 A 崩溃的唯一可能方式是 A 在它拥有互斥锁时崩溃。由于 B 将死锁,因此这种可能性非常低并且很容易观察到。更有可能的是,B 会愉快地继续前进,因为它现在完全畅通无阻,没有其他人会再获得互斥体。

此外,A 重新开始时的死锁证明了您已经知道的事情:B 出于某种原因永久拥有互斥锁。可能是因为它递归地获取了互斥锁。您知道这一点是因为您注意到您必须调用 ReleaseMutex 两次。这是您需要修复的错误。

您需要保护自己免受崩溃的兄弟进程的影响,并且需要为此编写显式代码。在同级上调用 OpenProcess 以获取进程对象的句柄。当进程终止时,句柄上的 WaitForSingleObject 调用将完成。

于 2013-02-20T18:10:39.690 回答
11

如果持有互斥锁的进程崩溃,那么它就会被放弃。如何处理从等待函数返回的状态取决于其他应用程序。

如果它WAIT_ABANDONED回来了,那么它可以继续进行,好像一切正​​常(大概是它现在所做的)或“可能不稳定的数据,谨慎行事”。所有权不会自动传递给另一个进程。

于 2013-02-20T17:25:55.727 回答