【问题标题】:Is there any reason to use a WaitHandle over a bool to flag for cancellation?是否有任何理由在 bool 上使用 WaitHandle 来标记取消?
【发布时间】:2011-03-22 19:15:04
【问题描述】:

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

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

【问题讨论】:

  • 您的停止方法需要调用bgThread.Join()

标签: .net multithreading synchronization waithandle


【解决方案1】:

写入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,最好使用任务取消机制:)

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

【讨论】:

  • 感谢 Jon - 不幸的是我仍然停留在 .NET 3.5 中;否则我会完全重写这个烂摊子。
  • 另一个问题 - 你提到使用 volatile - 将变量标记为 volatile 有什么缺点吗?
  • 您不能再通过“ref”使用该变量,并且您可能会失去可能的 CLR 优化,但这就是“volatile”的含义:)
  • @vtortola:您可以将该变量用作ref 参数,但您会收到警告,并且在调用的方法中不会保留波动性。
  • 这就是我的意思,我没有很好地解释自己:P
【解决方案2】:

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

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

【讨论】:

  • 就是这样——这段代码实际上并没有在这个等待句柄上wait,它只是将它用作一个标志。 +1 volatile 提示。
【解决方案3】:

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

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

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

【讨论】:

  • 我拥有的代码库中是 true - 老实说,我不明白这个论点,所以我不想偏离代码中的内容。跨度>
  • 忽略它,WaitOne() 有一个重载,它只需要超时。它被添加到 .NET 2.0 服务包 1 中,因为很多程序员也不知道它的含义并弄错了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-18
  • 2011-06-27
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 2015-12-04
  • 1970-01-01
相关资源
最近更新 更多