12

下面是一个具有“SomeMethod”方法的类,它说明了我的问题。

class SomeClass
{
    AutoResetEvent theEvent = new AutoResetEvent(false);
    // more member declarations

    public void SomeMethod()
    {
        // some code
        theEvent.WaitOne();
        // more code
    }
}

该方法被设计为线程安全的,将在不同的线程中调用。现在我的问题是,如何在任何时候解除对“theEvent”对象调用“WaitOne”方法的所有线程的阻塞?这个要求经常出现在我的设计中,因为我需要能够优雅地停止和启动我的多线程程序。在我看来,启动一个多线程程序相当简单,但要停止一个程序却很棘手。

这是我迄今为止尝试过的显然有效的方法。但这是标准方法吗?

public void UnblockAll()
{
    do
    {
        theEvent.Set();
    } while (theEvent.WaitOne(0));
}

“UnblockAll”方法是“SomeClass”类的成员。此处使用的技术基于WaitOne 方法的 MSDN 文档。我引用了以下文档的相关部分:

如果毫秒超时为零,则该方法不会阻塞。它测试等待句柄的状态并立即返回。

在 do..while 循环中,我调用Set 方法。这会释放一个可能由于调用 WaitOne 方法而阻塞的线程(编码在“SomeMethod”方法中)。接下来我测试“theEvent”对象的状态,以了解它是否已发出信号。该测试是通过调用带有超时参数的 WaitOne 方法的重载版本来完成的。我在调用 WaitOne 方法时使用的参数为零,根据文档,这会导致调用立即返回一个布尔值。如果返回值为真,则“theEvent”对象处于信号状态。如果在“SomeMethod”方法中调用“WaitOne”方法时至少有一个线程被阻塞,则对“Set”方法的调用(编码在“UnblockAll”方法中)将解除阻塞。因此,在“UnblockAll”方法中的 do..while 语句末尾调用“WaitOne”方法将返回 false。仅当没有线程阻塞时,返回值才为真。

上述推理是否正确,如果正确,该技术是否是处理我的问题的标准方法?我正在尝试主要在 .net compact-framework 2.0 平台上使用该解决方案。

4

3 回答 3

11

你有三个可行的选择。每个都有自己的优点和缺点。选择最适合您的具体情况的一种。

选项 1 - 轮询WaitHandle.

与其进行无限期阻塞调用,不如使用一个超时并在未给出关闭请求时恢复阻塞。

public void SomeMethod()
{
  while (!yourEvent.WaitOne(POLLING_INTERVAL))
  {
    if (IsShutdownRequested())
    {
      // Add code to end gracefully here.
    }
  }
  // Your event was signaled so now we can proceed.
}

选项 2 - 使用单独WaitHandle的请求关闭

public void SomeMethod()
{
  WaitHandle[] handles = new WaitHandle[] { yourEvent, shutdownEvent };
  if (WaitHandle.WaitAny(handles) == 1)
  {
    // Add code to end gracefully here.
  }
  // Your event was signaled so now we can proceed.
}

选项 3 - 使用Thread.Interrupt

不要将此与Thread.Abort. 中止线程肯定是不安全的,但中断线程则完全不同。Thread.Interrupt将“戳”BCL 中使用的内置阻塞调用,包括Thread.JoinWaitHandle.WaitOneThread.Sleep等。

于 2011-05-09T17:49:49.070 回答
3

您的例程可能大部分时间都可以工作,但我认为无法保证其中一个等待线程会在您的关闭循环设置它的时间和您的关闭循环再次检查它的时间之间重置事件。

我发现 AutoResetEvent 和 ManualResetEvent 类非常适合非常简单的场景。每当要求有任何奇怪之处时,我都会快速切换到更灵活的Wait And Pulse 模式

如果你不需要任何清理,你可以让你的工作线程成为后台线程,然后当主线程退出时它们就会停止。

您还可以定义第二个 ManualResetEvent,称为 stopRequest 并等待来自任一事件的信号。但是,紧凑的框架可能不支持。

于 2011-05-09T17:09:44.800 回答
1

线程中止是否适用于您的框架?

于 2011-05-09T17:34:12.740 回答