AutoResetEvent vs. boolean停止线程

时间:2012-08-14 13:22:53

标签: c# multithreading autoresetevent

我在工作线程中有一个对象,我可以指示它停止运行。我可以使用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的解决方案可能会更快停止,这些方法之间有什么区别吗?一个人比另一个“更好”吗?

5 个答案:

答案 0 :(得分:12)

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

所有这一切都深入discussed Eric Lippert(真的值得一读),这里是简短的引用:

  

在C#中,“volatile”不仅意味着“确保编译器和   抖动不执行任何代码重新排序或注册缓存   优化此变量“。它还意味着”告诉处理器   做任何他们需要做的事情,以确保我正在阅读   最新的价值,即使这意味着停止其他处理器和制造   它们将主内存与其缓存同步“

     

实际上,最后一点是谎言。 volatile读取的真正语义   写作比我在这里概述的要复杂得多;在   事实他们实际上并不保证每个处理器都会停止它   正在进行并更新主存储器的缓存。相反,他们提供   关于读取和读取之前和之后内存访问方式的较弱保证   可以观察到写入相对于彼此的命令。   某些操作,例如创建新线程,输入锁定或   使用Interlocked系列方法之一引入更强   关于观察订购的保证。如果你想要更多细节,   阅读C#4.0规范的第3.10和10.5.3节。

     

坦率地说,我不鼓励你做一个不稳定的领域。挥发物   字段表明你正在做一些彻头彻尾的疯狂:你是   尝试在两个不同的线程上读取和写入相同的值   没有锁定到位。锁保证内存读取或   在锁内部修改被观察到是一致的,锁保证   只有一个线程一次访问一个给定的内存块,等等   上。

答案 1 :(得分:7)

易失性不够好,但实际上它总是有效,因为操作系统调度程序最终会锁定。并且可以在具有强大内存模型的核心上运行良好,例如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,提供良好的性能和保证。

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

答案 2 :(得分:3)

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

答案 3 :(得分:1)

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

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


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


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

“boolean:”片段总是睡眠大约一秒钟,可能不是你想要的。

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

答案 4 :(得分:0)

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