分析/优化大量多线程应用程序

时间:2011-11-07 23:20:19

标签: c# .net multithreading

我正在编写一个性能至关重要的.NET应用程序,它大量使用多线程。

使用Visual Studio性能分析器,使用Exclusive样本的顶级函数是:

WaitHandle.WaitAny() - 14.23%

@JIT_MonReliableEnter@8 - 7.76%

Monitor.Enter - 5.09%

基本上,我的前3个函数正在使用线程原语,并且在某种程度上我不相信。相比之下,我的工作/处理程序非常小,我正在努力提高性能。我相信所涉及的算法非常合理,尽管我经常对它们进行审查。

我的问题是:

  • 如果这些方法中有14.23%的CPU样本 - 对于大多数样本来说,CPU是否有效“空闲”,即只是等待其他线程?或者是线程的空闲部分 - 等待未显示为配置文件跟踪的一部分[并且这些同步方法中所有开销的总和中的27.08%]? (我可以猜测这大部分是空闲的,但是会很感激这个问题背后的一些不错的参考资料)
  • 我已经审查了我的锁定方案,但这些结果是否表明我应该考虑进一步优化的一些特定瓶颈或技术?
  • WaitAny特别差吗?我大量使用它来检查特定队列对象是否可读/可写,还同时检查中止标志。有没有更好的方法呢?

2 个答案:

答案 0 :(得分:4)

当线程位于WaitHandle.WaitAnyMonitor.Enter时,您的CPU不一定是空闲的。处于等待状态的线程处于空闲状态,但可能其他线程正忙于执行。 Monitor.Enter尤其如此。如果一个线程在锁上被阻塞,那么人们就会希望拥有该锁的线程正在执行代码而不是闲置。

此外,如果您的线程正在使用WaitAny从队列中读取,那么队列中可能没有任何内容。这不是消费者代码的性能问题。它只是意味着生产者没有足够快地将东西放入队列中。现在,这可能是因为生产者很慢,或者因为数据进展不够快。

如果您处理的数据速度超过了它的速度,那么您看起来就不会遇到性能问题。当然不是消费者方面。

至于使用WaitAny进行排队,我建议您使用BlockingCollection以及采用取消令牌的方法,例如TryAdd(T, Int32, CancellationToken)。转换为取消令牌确实简化了我的多线程排队代码。

答案 1 :(得分:4)

分析统计信息包括线程被阻止的时间。

基于采样的探查器基本上要求每个核心在每个X(比如1,000,000)非空闲周期后报告。每次核心报告时,探查器都会记住当前的调用堆栈。分析结果是从分析器记录的调用堆栈重建的。

从分析结果中,您知道核心工作的时间占14.23%,它正在执行WaitHandle.WaitAny中的指令。如果您的程序受CPU限制,优化WaitAny部分(例如,使用不同的原语)可能会对性能产生重大影响。但是,如果程序受CPU限制并且大部分时间都在服务器,磁盘,另一个进程或其他一些外部输入上等待,那么优化WaitAny相关代码将不会很是有用的。

因此,您的下一步应该是弄清楚程序的CPU利用率是多少。另外,请注意Ilian提到的Concurrency Visualizer可以帮助您理解程序中的线程如何花费时间。