Sjo*_*erd 12 c# multithreading autoresetevent
我在工作线程中有一个对象,我可以指示它停止运行.我可以使用bool或AutoResetEvent来实现它:
布尔:
private volatile bool _isRunning;
public void Run() {
while (_isRunning)
{
doWork();
Thread.Sleep(1000);
}
}
Run Code Online (Sandbox Code Playgroud)
的AutoResetEvent:
private AutoResetEvent _stop;
public void Run() {
do {
doWork();
} while (!_stop.WaitOne(1000));
}
Run Code Online (Sandbox Code Playgroud)
Stop()然后该方法将设置_isRunning为false或调用_stop.Set().
除此之外,使用AutoResetEvent的解决方案可能会更快停止,这些方法之间有什么区别吗?这个比那个好吗?
ole*_*sii 12
C#volatile不提供所有保证.它仍然可以读取过时的数据.最好使用底层操作系统同步机制,因为它提供了更强大的保证.
Eric Lippert 讨论的所有这些都非常深入(非常值得一读),这里是简短的引用:
在C#中,"volatile"不仅意味着"确保编译器和抖动不对此变量执行任何代码重新排序或寄存器缓存优化".它还意味着"告诉处理器做他们需要做的任何事情,以确保我正在读取最新的值,即使这意味着停止其他处理器并使它们与主存储器的缓存同步".
实际上,最后一点是谎言.易失性读写的真正语义比我在此概述的要复杂得多; 实际上,它们实际上并不能保证每个处理器都停止正在进行的操作并更新主存储器的缓存.相反,它们提供了关于如何观察读取和写入之前和之后的存储器访问相对于彼此进行排序的较弱保证. 某些操作(如创建新线程,输入锁定或使用Interlocked系列方法)可以为观察排序提供更强有力的保证.如果您想了解更多详细信息,请阅读C#4.0规范的第3.10和10.5.3节.
坦率地说,我不鼓励你做一个不稳定的领域.易失性字段表明你正在做一些彻头彻尾的疯狂:你试图在两个不同的线程上读取和写入相同的值,而不是锁定到位.锁定保证锁内部读取或修改的内存一致,锁定保证一次只有一个线程访问给定的内存块,依此类推.
易失性不够好,但实际上它总是有效,因为操作系统调度程序最终会锁定.并且可以在具有强大内存模型的核心上运行良好,例如x86,它可以消耗大量的内存,以保持内核之间的缓存同步.
所以真正唯一重要的是线程响应停止请求的速度有多快.它很容易测量,只需在控制线程中启动一个秒表,并记录工作线程中while循环后的时间.我通过重复拍摄1000个样本并取平均值,重复10次测量结果:
volatile bool, x86: 550 nanoseconds
volatile bool, x64: 550 nanoseconds
ManualResetEvent, x86: 2270 nanoseconds
ManualResetEvent, x64: 2250 nanoseconds
AutoResetEvent, x86: 2500 nanoseconds
AutoResetEvent, x64: 2100 nanoseconds
ManualResetEventSlim, x86: 650 nanoseconds
ManualResetEventSlim, x64: 630 nanoseconds
Run Code Online (Sandbox Code Playgroud)
请注意,对于具有弱内存模型的处理器(如ARM或Itanium),volatile bool的结果不太可能看起来那么好.我没有人来测试.
很明显,你似乎想要支持ManualResetEventSlim,提供良好的性能和保证.
这些结果有一个注释,它们是在工作线程运行热循环的情况下测量的,不断测试停止条件而不做任何其他工作.这与真实代码并不完全匹配,线程通常不会经常检查停止条件.这使得这些技术之间的差异在很大程度上是无关紧要的.