3

我继承了一些线程代码,在查看它时,我发现了这样的结构(在后台线程方法中):

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();
}
4

3 回答 3

7

写入bool变量确实是原子的,但除非变量也是易失性的(或者您引入了其他一些同步),否则它可能在其他线程中不可见。此类问题可能难以追踪和重现。

例如,在我的 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,最好使用任务取消机制:)

当然,通常使用标志以外的东西的更好理由是,如果你想等待某些条件——无论是“有新项目”还是“我被取消了”(或两者兼而有之)——没有紧密循环。

于 2011-03-22T19:25:14.660 回答
1

您不能对 bool 进行受控/阻塞“等待”(尽管您可能不需要,而且 Monitor 可能更轻量级)。

作为取消标志bool 很好,但您可能希望使 bool 易失性以防止寄存器缓存成为问题。

于 2011-03-22T19:28:18.637 回答
1

仅使用 bool 是行不通的,您必须将其声明为volatile以告诉代码生成器它不应将值存储在 CPU 寄存器中。线程将看到值设置为 true 的确切时间是不可预测的,这在很大程度上取决于您的代码运行的 CPU 类型。

丑陋的细节,使用 WaitHandle 时可以忽略的细节。

最后一个参数应该是 False btw。

于 2011-03-22T19:32:31.533 回答