检测C#应用程序中的死锁

可能重复:
C#/ .NET分析工具查找竞争条件/死锁

我正在调试一个应用程序,我怀疑它正在陷入僵局并挂起。 但是,这只会每隔几天发生一次,并且它永远不会发生在我的计算机上,因此我无法将调试器挂钩。 是否有任何实用程序或方法可用于查询正在运行的应用程序并找出哪些方法/锁定/它是什么死锁?

更新:通常应用程序在客户位置运行,我无法访问该计算机,并且我不太愿意让他们安装大量软件。

您可以使用“TimedLock”结构,而不是使用常规lockMonitor.Enter方法来锁定某些数据。 如果无法及时获取锁定,则此TimedLock会抛出exception,如果您有一些未释放的锁定,它也会发出警告。

Ian Griffiths撰写的这篇文章可能会有所帮助。

您可以使用WinDbg检查应用程序中的线程。 以下是您可以做的简要计划。

  • 应用程序挂起时,将WinDbg文件复制到计算机。
  • 将WinDbg附加到进程或使用ADPlus获取进程的挂起转储。 如果选择ADPlus,则在WinDbg中加载转储。
  • 从WinDbg加载sos.dll,以便检查托管代码。
  • !threads命令将显示应用程序中的所有线程,并且!clrstack命令将显示它们正在执行的操作。 使用~e!clrstack转储所有线程的调用堆栈。 查找Wait方法的调用,因为它们表示锁定。
  • !syncblk命令将为您提供有关哪些线程持有不同锁的信息。
  • 要找出给定线程尝试获取的锁定,请切换到该线程并检查堆栈对象( !dso )。 从这里你应该能够找到线程试图获取的锁。

澄清:WinDbg不需要定期安装。 只需复制文件。 此外,如果您执行挂起转储,则可以根据需要继续在另一台计算机上进行调试。

增加: Sosex具有!dlk命令,可在许多情况下自动识别死锁。 它不会一直有效,但是当它发生时,它会为你完成所有的工作,所以这应该是你的第一选择。

并发编程中的超时是一个可怕的想法。 这导致非确定性,从而导致无法再现的行为。 尝试使用像CHESS这样的死锁检测工具。 更好的是,最小化无锁算法使用的锁的数量,或完全避开锁并将程序划分为单线程隔离区并使用队列在隔离专区之间传递数据(更好地称为消息传递/ actor并发)。

你实际上有一个非常有趣的问题。 你可以做几件事:

使用一个好的记录器:重现multithreading错误的方法之一是使用一个记录器来打印所采取的操作和执行它们的线程,这样你就可以找到跟踪你的错误指南。 如果您可以添加记录器,这是一个相当简单的解决方案。

使用FSP:使用FSP定义multithreading系统。 这样,您就可以创建流程的有限状态机,您可以通过它来查找错误。 该解决方案是更加数学的解决方案。

我给你的两个解决方案/程序正是一些英国大学和Amercian之间接近multithreading发展的主要区别。 在英国,教授们更善于尝试并certificate他们的系统在编程之前没有使用FSP的错误,并且美国人更喜欢测试certificate他们正常工作,这是一个品味问题。

我真的建议你阅读这本书:Jeff Magee和Jeff Kramer:并发:状态模型和Java程序,Wiley,1999

这是一个非常有趣的问题和痛苦,因为它每隔几天就会发生一次。 我在CodeProject上发现了这篇文章 。 这可能是一个开始。

旧学校的方法是记录大量消息并使用日志文件来尝试检测它何时发生。 🙂

除了这里的答案之外,您通常会发现线程编程有用的另一件事是确保您的开发框是多处理器机器,特别是死锁(通常)更可靠地再现。