debugging 多线程调试技术

hujrc8aj  于 2023-04-30  发布在  其他
关注(0)|答案(5)|浏览(146)

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

2w2cym1i

2w2cym1i1#

我不知道有哪篇文章或哪本书能满足您的需求,所以这里是我从12年的Windows多线程调试(非托管和托管)中“学到的教训”。
正如我在评论中所说的,我的大多数“多线程调试”实际上是通过手动代码审查来完成的,寻找这些问题。

死锁和损坏的共享状态

记录lock hierarchies(顺序和它们保护的共享状态),并确保它们是一致的。这解决了大多数死锁问题和损坏的共享状态问题。
(Note:上面的“锁层次结构”的链接是指Dr.多布斯文章由赫伯萨特;他写了一系列我强烈推荐的Effective Concurrency文章)。

关于死锁的更多信息

使用RAII for all synchronization。这确保了在出现异常时释放锁。使用“lock”语句而不是try/finally。
(Note那个RAIINET依赖于IDisposable,而不是Finalize,并假设客户端代码将正确地使用using块)。

饥饿

删除对线程优先级的任何修改。正确的优先级排序实际上有点违反直觉:最好给予那些工作量最大的线程一个较低的优先级,而给那些I/O受限的线程(包括UI线程)一个较高的优先级。由于Windows会自动执行此操作(请参阅Windows Internals),因此实际上根本没有理由让代码参与其中。

一般情况

删除内部编写的所有无锁代码。它几乎肯定包含细微的bug。替换为。NET 4 lock-free collectionssynchronization objects,或者将代码更改为基于锁的。
使用更高级别的概念进行同步。中的Task Parallel Libraryunified cancellation。NET 4几乎不需要直接使用ManualResetEventMonitorSemaphore等。
使用高级概念进行并行化。中的TPL and PLINQNET 4具有内置的自平衡算法,包括智能分区和工作窃取队列,以自动提供最佳并行化。对于自动并行化是次优的少数情况,TPL和PLINQ都暴露了大量可调整的旋钮(自定义分区方案,长时间运行的操作标志等)。
我发现还有一种技术对任何方法被不同线程调用的类都很有用:记录哪些方法在哪些线程上运行。通常,这是作为注解添加到方法的顶部。确保每个方法只在已知的线程上下文中运行(例如例如,“在UI线程上”或“在ThreadPool线程上”或“在专用后台线程上”)。没有一个方法应该说“在任何线程上”,除非你正在编写一个同步类(如果你正在编写一个同步类,问问你自己是否真的应该这样做)。
最后,命名你的线程。这有助于在使用VS调试器时轻松区分它们。.NET通过Thread.Name属性支持这一点。

14ifxucb

14ifxucb2#

不是你想要的,但也许你觉得CHESS很有趣。

cnh2zyt3

cnh2zyt33#

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

ryhaxcpt

ryhaxcpt4#

我使用了Valgrind的子工具Helgrind。Helgrind是一个线程错误检测器,我曾经用过一两次它来检测我的一些代码中的竞态条件。它可以检测到以下事情。
1.错误使用POSIX pthreads API。
1.由锁排序问题引起的潜在死锁。
1.数据竞争--在没有充分锁定或同步的情况下访问内存。
http://valgrind.org/docs/manual/hg-manual.html
显然只有linux工具的系统程序,C / C++。没有Java或。NET.

kfgdxczn

kfgdxczn5#

我不认为有任何技术可以可靠地检测所有的多线程问题,因为导致这些问题的代码太复杂了,难以分析。没有工具能够真实的检测到这样的问题,因为工具本身也需要时间来运行。要调试的程序在使用该工具和不使用该工具时的行为完全不同。
我不得不调试真实的问题,而这些问题在生产中一个月只发生一次!我找到的唯一解决方案是添加检测该问题的代码,并按所涉及的线程编写跟踪信息。当然,跟踪必须非常快,并且不阻塞。像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

相关问题