多线程 – Delphi:通过报告锁定“失败”的运行线程的调用堆栈来调试关键部分挂起

我正在寻找一种方法来调试罕见的Delphi 7临界区(TCriticalSection)挂起/死锁.在这种情况下,如果一个线程正在等待临界区超过10秒,我想生成一个报告,其中包含当前锁定临界区的线程的堆栈跟踪以及无法执行的线程在等待10秒后锁定关键部分.如果引发异常或应用程序终止,则可以.

如果可能的话,我宁愿继续使用关键部分,而不是使用其他同步原语,但如果需要可以切换(例如获取超时功能).

如果工具/方法在IDE之外的运行时工作,这是一个奖励,因为这很难按需重现.在极少数情况下,我可以在IDE中复制死锁,如果我尝试暂停以开始调试,那么IDE只会在那里无所事事,并且永远不会进入我可以查看线程或调用堆栈的状态.不过,我可以重置正在运行的程序.

更新:在这种情况下,我只处理一个关键部分和2个线程,所以这可能不是锁定排序问题.我相信有一个不正确的嵌套尝试跨越两个不同的线程进入锁定,这会导致死锁.

最佳答案
您应该创建并使用自己的锁对象类.它可以使用关键部分或互斥锁来实现,具体取决于您是否要调试它.

创建自己的类有一个额外的好处:您可以实现锁定层次结构并在违反时引发异常.每次锁定不完全相同时,就会发生死锁.为每个锁分配锁级别可以检查锁是否按正确的顺序进行.您可以将当前锁定级别存储在threadvar中,并且仅允许锁定具有较低锁定级别的锁定,否则会引发异常.这将捕获所有违规,即使没有发生死锁,所以它应该加快你的调试速度.

至于获取线程的堆栈跟踪,Stack Overflow上有很多问题可以解决这个问题.

更新

你写:

In this case, I’m only dealing with one critical section and 2 threads, so this likely isn’t a lock ordering problem. I believe there is an improper nested attempt to enter the lock across two different threads, which results in deadlock.

这不可能是整个故事.在Windows上无法单独使用两个线程和一个关键部分进行死锁,因为线程可以递归地获取关键部分.必须涉及另一种阻塞机制,例如SendMessage()调用.

但是如果你真的只处理两个线程,那么其中一个必须是主/ VCL / GUI线程.在这种情况下,您应该能够使用MadExcept “Main thread freeze checking”功能.它将尝试向主线程发送消息,并在经过可自定义的时间后失败,而不处理消息.如果主线程在关键部分阻塞,而另一个线程在消息处理调用上阻塞,那么MadExcept应该能够捕获这个并为两个线程提供堆栈跟踪.

转载注明原文:多线程 – Delphi:通过报告锁定“失败”的运行线程的调用堆栈来调试关键部分挂起 - 代码日志