多线程调试技术

时间:2010-07-05 13:02:14

标签: .net multithreading debugging

我想知道是否有人知道对多线程应用程序的调试技术的一个很好的调查。理想情况下,我正在寻找基于案例的分析:死锁,饥饿,共享状态损坏,......

.Net特定或通用。

5 个答案:

答案 0 :(得分:11)

我不知道有哪篇文章或书籍可以解决您所寻找的问题,因此这是我在Windows上进行12年多线程调试(非托管和托管)的“经验教训”。

正如我在评论中所述,我的大多数“多线程调试”实际上是通过手动代码审查完成的,并且正在寻找这些问题。

死锁和共享状态损坏

文档lock hierarchies(订单和他们保护的共享状态),并确保它们是一致的。这解决了大多数死锁问题和损坏的共享状态问题。

(注意:上面的“锁定层次结构”链接指的是Herb Sutter的Dr. Dobbs文章;他撰写了一系列我强烈推荐的Effective Concurrency篇文章。

有关死锁的详情

使用RAII for all synchronization。这可确保在异常情况下释放锁定。首选“lock”语句尝试/ finally。

(请注意,.NET中的RAII取决于IDisposable,而不是Finalize,并假设客户端代码将正确使用using块。

<强>饥饿

删除线程优先级的任何修改。正确的优先级实际上有点反直觉:最好给线程最多的工作来做较低的优先级,并为I / O绑定的线程(包括UI线程)提供更高的优先级。由于Windows会自动执行此操作(请参阅Windows Internals),因此根本没有理由让代码参与其中。

一般

删除内部编写的所有无锁代码。它几乎肯定包含微妙的错误。将其替换为.NET 4 lock-free collectionssynchronization objects,或将代码更改为基于锁定。

使用更高级别的概念进行同步。 .NET 4中的Task Parallel Libraryunified cancellation几乎不需要直接使用ManualResetEventMonitorSemaphore等。

使用更高级别的概念进行并行化。 .NET 4中的TPL and PLINQ具有内置的自平衡算法,具有智能分区和工作窃取队列,可自动提供最佳的并行化。对于极少数情况,自动并行化是次优的,TPL和PLINQ都会暴露出大量可调整的旋钮(自定义分区方案,长时间运行的操作标志等)。

我发现还有一种技术可用于任何具有不同线程调用方法的类:记录哪些方法在哪些线程上运行。通常,这会作为注释添加到方法的顶部。确保每个方法仅在已知的线程上下文中运行(例如,“在UI线程上”或“在ThreadPool线程上”或“在专用后台线程上”)。除非您正在编写同步类,否则所有方法都不应该说“在任何线程上”(如果您正在编写同步类,请问自己是否真的应该这样做。)

最后,为你的主题命名。这有助于在使用VS调试器时轻松区分它们。 .NET通过Thread.Name属性支持此功能。

答案 1 :(得分:6)

不是你要求的,但也许你会发现CHESS有趣。

答案 2 :(得分:1)

您还可以查看英特尔的Thread CheckerThread ProfilerSun's Studio Thread Analyzer,但它们不是免费的。另请查看英特尔的this文章。

答案 3 :(得分:0)

我使用过Helgrind作为Valgrind的子工具。 Helgrind是一个线程错误检测器,我使用它一次或两次来检测我的一些代码中的竞争条件。它可以检测到以下内容。

  1. 错误使用POSIX pthreads API。
  2. 锁定排序问题引起的潜在死锁。
  3. 数据争用 - 在没有足够锁定或同步的情况下访问内存。
  4. http://valgrind.org/docs/manual/hg-manual.html

    显然只有linux工具用于系统程序,C / C ++。没有Java或.NET。

答案 4 :(得分:0)

我不认为任何技术都可以可靠地检测所有多线程问题,因为导致它们的代码太复杂而无法分析。没有工具可以实时检测到这些问题,因为工具本身也需要时间来运行。调试程序与工具完全不同,没有。

我不得不调试每月只在生产中发生一次的实时问题!我发现的唯一解决方案是添加检测该问题的代码并通过所涉及的线程写入跟踪信息。当然,跟踪必须非常快速且无阻塞。像Visual Studio这样的常用工具对于实时跟踪来说太慢了,但幸运的是,编写自己的内存跟踪很容易:

const int maxMessages = 0x100;
const int indexMask = maxMessages-1;
string[] messages = new string[maxMessages];
int messagesIndex = -1;

public void Trace(string message) {
  int thisIndex = Interlocked.Increment(ref messagesIndex) & indexMask;
  messages[thisIndex] = message;
}

这种方法的更详细描述也收集了线程和时序信息并很好地输出了跟踪: CodeProject:实时调试多线程代码1