代码之家  ›  专栏  ›  技术社区  ›  Mau

多线程调试技术

  •  6
  • Mau  · 技术社区  · 14 年前

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

    .NET特定或通用。

    5 回复  |  直到 8 年前
        1
  •  11
  •   Stephen Cleary    14 年前

    我不知道有一篇文章或一本书介绍了您要查找的内容,所以这里是我在Windows上进行12年多线程调试(非托管和托管)的“经验教训”。

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

    死锁和损坏的共享状态

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

    (注:“锁层次”的链接是指Herb Sutter的Dobbs博士文章,他写了一系列 Effective Concurrency 我强烈推荐的文章)。

    更多关于死锁的内容

    使用 RAII for all synchronization . 这样可以确保在出现异常时释放锁。最好使用“lock”语句来尝试/最终。

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

    饥饿

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

    一般来说

    删除内部编写的所有无锁代码。几乎可以肯定,它包含一些细微的错误。替换为.NET 4 lock-free collections synchronization objects 或者将代码更改为基于锁的。

    使用更高级的概念进行同步。这个 Task Parallel Library unified cancellation 在.NET 4中,几乎不需要直接使用 ManualResetEvent , Monitor , Semaphore 等。

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

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

    最后,命名线程。这有助于在使用vs调试器时轻松区分它们。.NET通过 Thread.Name 财产。

        2
  •  6
  •   Daniel Brückner Pradip    14 年前

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

        3
  •  1
  •   the_void    14 年前

    你也可以看看英特尔的 Thread Checker Thread Profiler Sun's Studio Thread Analyzer 尽管它们不是免费的。也签出 this 来自英特尔的文章。

        4
  •  0
  •   bradgonesurfing    14 年前

    我用了赫尔格里的一个子工具。Helgrind是一个线程错误检测器,我用它在我的一些代码中检测过一两次竞争条件。它可以检测到以下情况。

    1. 误用posix pthreads API。
    2. 锁排序问题可能导致死锁。
    3. 数据竞争——在没有足够锁定或同步的情况下访问内存。

    http://valgrind.org/docs/manual/hg-manual.html

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

        5
  •  0
  •   Peter Huber    8 年前

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

    我必须调试在生产中发生的实时问题,每月只有一次!我找到的唯一解决方案是添加代码检测该问题,并通过所涉及的线程写入跟踪信息。当然,跟踪必须非常快速且无阻塞。像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;
    }
    

    对于这种方法的更详细的描述是,它还收集线程和时间信息,并很好地输出跟踪信息,如下所示: 代码项目:实时调试多线程代码 1