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