AutoResetEvent vs. boolean停止线程

我在工作线程中有一个对象,我可以指示它停止运行。 我可以使用bool或AutoResetEvent来实现它:

布尔:

private volatile bool _isRunning; public void Run() { while (_isRunning) { doWork(); Thread.Sleep(1000); } } 

的AutoResetEvent:

 private AutoResetEvent _stop; public void Run() { do { doWork(); } while (!_stop.WaitOne(1000)); } 

然后Stop()方法将_isRunning设置为false,或调用_stop.Set()

除此之外,使用AutoResetEvent的解决方案可能会更快停止,这些方法之间有什么区别吗? 这个比那个好吗?

C# volatile不提供所有保证。 它仍然可以读取过时的数据。 最好使用底层的OS同步机制,因为它提供了更强大的保证。

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 

请注意,对于具有弱内存模型的处理器(如ARM或Itanium),volatile bool的结果不太可能看起来那么好。 我没有人来测试。

很明显,你似乎想要支持ManualResetEventSlim,提供良好的性能和保证。

这些结果有一个注释,它们是在工作线程运行热循环的情况下测量的,不断测试停止条件而不做任何其他工作。 这与真实代码并不完全匹配,线程通常不会经常检查停止条件。 这使得这些技术之间的差异在很大程度上是无关紧要的。

恕我直言AutoResetEvent更好,因为在这种情况下你不能忘记关键的volatile关键字。

在使用volatile关键字之前,您应该阅读本文 ,我想,在研究multithreading时,您可以阅读整篇http://www.albahari.com/threading/文章。

它解释了volatile关键字的细微之处以及为什么它的行为可能出乎意料。


您会注意到,当使用volatile ,读取和写入可以重新排序,这可能导致在紧密并发的情况下进行额外的迭代。 在这种情况下,您可能需要等待大约一秒钟。


看了之后,我不认为你的代码有多种原因,

“boolean:”代码段总是会hibernate大约一秒钟,可能不是你想要的。

“AutoResetEvent:”代码段不会实例化_stop并且将始终至少运行一次doWork()

这取决于上面的片段是否就是您正在做的事情。 顾名思义,AutoReset事件在WaitOne传递后会重置。 这意味着你可以直接再次使用它,而不是使用bool,必须将其设置为true。