调试难以重现的并发错误的提示?

时间:2011-04-28 13:54:31

标签: multithreading debugging language-agnostic concurrency

有哪些提示可以很难重现并发错误,例如每千次测试一次就会发生?我有其中一个,我不知道如何调试它。我无法在所有地方放置打印语句或调试器手表以观察内部状态,因为这会改变时间并在错误未成功复制时产生大量信息。

8 个答案:

答案 0 :(得分:5)

这是我的技术:我通常使用很多assert()来尽可能多地检查数据的一致性/有效性。当一个断言失败时,程序崩溃生成核心文件。然后我使用带有核心文件的调试器来了解导致数据损坏的线程配置。

答案 1 :(得分:2)

这可能对您没有帮助,但可能有助于将来看到此问题。

如果您使用的是.Net语言,则可以使用Microsoft研究中的CHESS项目。它使用各种线程交错运行单元测试,并显示哪些导致错误发生。

您使用的语言可能有类似的工具。

答案 2 :(得分:1)

这在很大程度上取决于问题的性质。通常有用的是二分(缩小搜索空间)+代码“检测”,带有用于访问线程ID,锁定/解锁计数,锁定顺序等的断言,希望当问题将在下次应用程序将重现时重现一个详细的消息或核心转储为您提供解决方案。

答案 3 :(得分:1)

查找由并发错误导致的数据损坏的一种方法:

  • 为该数据或缓冲区添加原子计数器。
    • 保留所有现有的同步代码 - 不要修改它们,假设您要修复现有代码中的错误,而修复错误后将删除新的原子计数器。
  • 开始修改数据时,递增原子计数器。完成后,减少。
  • 一旦发现计数器大于1,就会进行核心转储(使用类似于InterlockedIncrement的东西)

答案 4 :(得分:0)

根据我的经验,有针对性的单元测试代码很耗时但很有效。

尽可能地缩小失败的代码。编写特定于明显罪魁祸首代码的测试代码,并在调试器中运行它,只要重现问题即可。

答案 5 :(得分:0)

我使用的策略之一是通过引入旋转等待来模拟线程的交错。需要注意的是,您不应该为您的平台使用标准的旋转等待机制,因为它们可能会引入内存障碍。如果您尝试进行故障排除的问题是由于缺少内存屏障(因为在使用无锁策略时难以纠正障碍),那么标准的自旋等待机制将掩盖问题。相反,在您希望代码暂停的位置放置一个空循环。这可能会增加再现并发错误的可能性,但它不是一个神奇的子弹。

答案 6 :(得分:0)

如果错误是死锁,只需在发生死锁后将调试工具(如gdbstrace)附加到程序,并观察每个线程的位置卡住了,通常可以为您提供足够的信息来快速追踪错误的来源。

答案 7 :(得分:0)

我在调试多线程代码时需要考虑一些调试技术的小图表。图表正在增长,请留下评论和提示添加。 http://adec.altervista.org/blog/multithreading-debugging-chart/