我们有几年的代码库,所有原始开发人员早已不复存在。它使用许多线程,但没有明显的设计或通用的架构原则。每个开发人员都有自己的多线程编程风格,因此一些线程使用队列相互通信,一些锁定数据与互斥锁,一些锁定信号量,一些使用操作系统IPC机制进行进程内通信。没有设计文档,评论很少。这是一团糟,似乎每当我们尝试重构代码或添加新功能时,我们都会引入死锁或其他问题。
那么,有没有人知道任何有助于分析和记录线程之间所有交互的工具或技术? FWIW,代码库是Linux上的C ++,但我很想知道其他环境的工具。
我很欣赏到目前为止收到的回复,但我希望能提供更复杂或更系统的内容,而不是基本上是“添加日志消息,弄清楚发生了什么,并修复它”的建议。有许多工具可用于分析和记录单线程程序中的控制流程;没有什么可用于多线程程序?
答案 0 :(得分:6)
投资英特尔VTune及其线程分析工具的副本。它将为您提供线程行为的系统和源级视图。它肯定不会为你自动记录,但应该是一个真正的帮助,至少可视化在不同情况下发生的事情。
我认为您可以下载试用版,因此可能值得一试。我只使用了Windows版本,但是看一下VTune网页,它也有一个Linux版本。
答案 1 :(得分:4)
作为一个起点,我很想在应用程序的关键点添加跟踪日志消息。这将允许您分析线程如何交互,而不会有观察线程的行为会改变其行为的危险(可能是逐步调试的情况)。 我的经验是使用.NET平台,我最喜欢的日志记录工具是log4net,因为它是免费的,有广泛的配置选项,如果你对如何实现日志记录很明智,它不会明显阻碍你的应用程序的性能。或者,在System.Diagnostics命名空间中有.NET内置的Debug(或Trace)类。
答案 2 :(得分:3)
我首先关注共享内存锁(互斥锁和信号量),因为它们最有可能导致问题。查看哪个状态受锁保护,然后确定哪个状态受到多个锁的保护。这会让你感觉到潜在的冲突。查看持有锁的代码调用方法的情况(不要忘记虚方法)。尝试尽可能消除这些调用(通过减少锁定的时间)。
给定保留的互斥锁列表以及它们保护的状态的粗略概念,指定一个锁定顺序(即,应始终在互斥锁B之前执行互斥锁A)。尝试在代码中强制执行此操作。
如果并发性不会受到不利影响,请查看是否可以将多个锁组合成一个。例如,如果互斥锁A和B看起来像死锁并且订购方案不容易,最初将它们组合到一个锁中。
这并不容易,但我是以牺牲并发性为代价来简化代码以解决问题。
答案 3 :(得分:2)
对于自动化工具来说,这是一个非常难的问题。您可能希望查看model checking您的代码。不要指望神奇的结果:模型检查器的代码量和它们可以有效检查的线程数量非常有限。
可能适合您的工具是CHESS(虽然不幸的是仅限Windows)。 BLAST是另一个相当强大的工具,但是很难使用,可能无法处理C ++。维基百科还列出了StEAM,我之前没有听说过,但听起来它可能适合你:
StEAM是C ++的模型检查器。它可以检测死锁,分段错误,超出范围的变量和非终止循环。
或者,尝试将代码汇聚到少量明确定义的(并且优选地,高级)同步方案可能会有很大帮助。在相同的代码库中混合锁,信号量和监视器会引发麻烦。
答案 4 :(得分:1)
使用log4net或类似工具时要记住的一件事是它们会改变应用程序的时间并且通常可以隐藏潜在的竞争条件。我们有一些写得不好的代码来调试和引入日志记录,这实际上消除了竞争条件和死锁(或者大大降低了它们的频率)。
答案 5 :(得分:1)
在Java中,您可以选择FindBugs(用于静态字节码分析)来查找某些类型的不一致同步,或者选择Coverity,JProbe,OptimizeIt等公司的许多动态线程分析器。
答案 6 :(得分:1)
UML不能帮助你吗?
如果您将代码库反向工程为UML,那么您应该能够绘制显示类之间关系的类图。从方法是线程入口点的类开始,您可以看到哪个线程使用哪个类。根据我对Rational Rose的经验,这可以通过拖放来实现;如果添加的类与之前的类之间没有关系,那么添加的类不会被以您开始使用该方法的方法开头的线程直接使用。这应该为您提供每个线程角色的提示。
这也将显示共享的“数据对象”和特定于线程的对象。
如果你绘制一个大类图并删除所有“数据对象”,那么你应该能够将该图布局为云,每个云都是一个线程 - 或一组线程,除非耦合和凝聚力代码库很糟糕。
这只会给你一部分拼图,但它可能会有所帮助;我只是希望你的代码库不是太混乱或太“程序化”,在这种情况下......