我们的代码库已经有好几年了,所有原来的开发人员都早已不在了。它使用很多很多线程,但没有明显的设计或通用的架构原则。每个开发人员都有自己的多线程编程风格,因此有些线程使用队列相互通信,有些线程使用互斥体锁定数据,有些使用信号量锁定,有些使用操作系统 IPC 机制进行进程内通信。没有设计文档,评论也很少。这是一团糟,似乎每当我们尝试重构代码或添加新功能时,都会引入死锁或其他问题。

那么,有人知道有什么工具或技术可以帮助分析和记录线程之间的所有交互吗?FWIW,代码库是 Linux 上的 C++,但我有兴趣了解其他环境的工具。


更新

我感谢到目前为止收到的回复,但是我希望与“添加日志消息”,弄清楚发生了什么并修复它的建议更复杂或系统地进行。”有很多工具可以在单线程程序中分析和记录控制流。没有什么可以用于多线程程序吗?


也可以看看 调试多线程应用程序

有帮助吗?

解决方案

投资一份英特尔的副本 VTune 及其线程分析工具。它将为您提供线程行为的系统和源级别视图。它当然不会为您自动记录事情,但至少应该在可视化不同情况下发生的情况方面提供真正的帮助。

我认为您可以下载试用版,因此值得一试。我只使用过Windows版本,但是查看VTune网页它也有Linux版本。

其他提示

作为起点,我很想在应用程序中的战略点添加跟踪日志消息。这将使您能够分析线程如何交互,而不会出现观察线程的行为会改变其行为的危险(就像逐步调试的情况一样)。我的经验是使用 .NET 平台,我最喜欢的日志工具是 log4net,因为它是免费的,具有广泛的配置选项,并且如果您明智地实现日志记录,它不会明显影响应用程序的性能。或者,System.Diagnostics 命名空间中有 .NET 内置的 Debug(或 Trace)类。

我首先关注共享内存锁(互斥锁和信号量),因为它们最有可能导致问题。查看哪个状态受到锁的保护,然后确定哪个状态受到多个锁的保护。这会让你感觉到潜在的冲突。查看持有锁的代码调用方法的情况(不要忘记虚拟方法)。尝试尽可能消除这些调用(通过减少持有锁的时间)。

给定所持有的互斥体列表以及它们所保护的状态的粗略概念,分配锁定顺序(即,互斥体 A 应始终在互斥体 B 之前获取)。尝试在代码中强制执行这一点。

如果并发性不会受到不利影响,看看是否可以将多个锁合并为一个。例如,如果互斥锁 A 和 B 看起来可能存在死锁,并且排序方案不容易完成,那么首先将它们组合成一个锁。

这并不容易,但我会以牺牲并发性为代价来简化代码来解决问题。

对于自动化工具来说,这确实是一个难题。您可能想了解一下 模型检验 你的代码。不要期待神奇的结果:模型检查器的代码量和它们可以有效检查的线程数非常有限。

可能适合您的工具是 (尽管不幸的是它仅适用于Windows)。 爆破 是另一个相当强大的工具,但非常难以使用,并且可能无法处理 C++。维基百科还列出了 蒸汽, ,我以前没听说过,但听起来它可能对你有用:

StEAM 是 C++ 的模型检查器。它可以检测死锁、分段错误、超出范围的变量和非终止循环。

或者,尝试将代码收敛到少量定义良好(并且最好是高级)的同步方案可能会有很大帮助。在同一个代码库中混合使用锁、信号量和监视器会带来麻烦。

使用 log4net 或类似工具需要记住的一件事是,它们会改变应用程序的时间,并且通常会隐藏潜在的竞争条件。我们有一些写得不好的代码需要调试,并引入了日志记录,这实际上消除了竞争条件和死锁(或大大降低了它们的频率)。

在 Java 中,您可以选择 FindBugs(用于静态字节码分析)来查找某些类型的不一致同步,或者选择来自 Coverity、JProbe、OptimizeIt 等公司的许多动态线程分析器。

UML 不能帮助您吗?

如果您将代码库逆向工程为 统一建模语言, ,那么您应该能够绘制显示类之间关系的类图。从其方法作为线程入口点的类开始,您可以看到哪个线程使用哪个类。根据我的经验 理性玫瑰, ,这可以通过拖放来实现;如果添加的类与之前的类之间没有关系,则添加的类不会直接由以您开始图表的方法启动的线程使用。这应该会提示您每个线程的作用。

这还将显示共享的“数据对象”和线程特定的对象。

如果您绘制一个大的类图并删除所有“数据对象”,那么您应该能够将该图布局为云,每个云都是一个线程或一组线程,除非代码库的耦合和内聚性是可怕。

这只会为您提供拼图的一部分,但它可能会有所帮助;我只是希望你的代码库不要太混乱或太“程序化”,在这种情况下......

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top