我继承了一些线程代码,在查看它之后,我发现了这样的结构(在后台线程方法中):
private ManualResetEvent stopEvent = new ManualResetEvent(false);
private void Run_Thread() {
while (!stopEvent.WaitOne(0, true)) {
// code here
}
}
通常有公共或私有Stop()
方法,如下所示:
public void Stop() {
stopEvent.Set();
bgThread.Join();
}
我的问题是:在这里使用等待句柄可以提供什么?似乎这样做是为了确保停止的信号是原子操作,但我认为写入布尔值无论如何都是原子的。如果是这种情况,是否有任何理由不使用以下内容:
private void Run_Thread() {
while(!stop) {
// code here
}
}
public void Stop() {
stop = true;
bgThread.Join();
}
答案 0 :(得分:7)
写入bool
变量确实是原子的,但除非该变量也是 volatile (或者你引入了一些其他同步),否则它可能在其他线程中不可见。这些问题很难追查和重现。
例如,在我的x64笔记本电脑上,以下程序正确停止。在我的x86上网本上,它永远挂起。 (两者均使用csc /o+ Test.cs
编译。)
using System;
using System.Threading;
class Test
{
static bool stop = false;
static void Main(string[] args)
{
new Thread(CountLots).Start();
Thread.Sleep(100);
stop = true;
Console.WriteLine("Finished...");
}
static void CountLots()
{
long total = 0;
while (!stop)
{
total++;
}
}
}
在这种特殊情况下,使用volatile标志似乎是合理的 - 尽管如果你使用.NET 4,最好使用Task取消机制:)
当然,通常使用旗帜以外的其他东西的更好理由是,如果你想等待一些条件 - 无论是“有新物品”还是“我被取消”(或两者都有) - 没有紧张 - 循环。
答案 1 :(得分:1)
你不能对bool进行控制/阻止“等待”(尽管你可能不需要,而且监视器可能更轻量级)。
作为取消标志 bool很好,但你可能想让bool变得易失,以防止寄存器缓存成为问题。
答案 2 :(得分:1)
只是使用bool不起作用,你必须声明它 volatile 告诉代码生成器它不应该将值存储在CPU寄存器中。线程将值设置为true的确切时间是不可预测的,这在很大程度上取决于代码运行的CPU类型。
丑陋的细节,当您使用WaitHandle时可以忽略的细节。
最后一个参数应为False btw。