我正在编写一个性能至关重要的.NET应用程序,它大量使用多线程。
使用Visual Studio性能分析器,使用Exclusive样本的顶级函数是:
WaitHandle.WaitAny()
- 14.23%
@JIT_MonReliableEnter@8
- 7.76%
Monitor.Enter
- 5.09%
基本上,我的前3个函数正在使用线程原语,并且在某种程度上我不相信。相比之下,我的工作/处理程序非常小,我正在努力提高性能。我相信所涉及的算法非常合理,尽管我经常对它们进行审查。
我的问题是:
WaitAny
特别差吗?我大量使用它来检查特定队列对象是否可读/可写,还同时检查中止标志。有没有更好的方法呢?答案 0 :(得分:4)
当线程位于WaitHandle.WaitAny
或Monitor.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可以帮助您理解程序中的线程如何花费时间。