如何取消阻止在AutoResetEvent对象上调用WaitOne方法的线程?

ghd*_*ghd 12 .net c# waithandle waitone compact-framework2.0

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

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

    public void SomeMethod()
    {
        // some code
        theEvent.WaitOne();
        // more code
    }
}
Run Code Online (Sandbox Code Playgroud)

该方法设计为线程安全的,并将在不同的线程中调用.现在我的问题是如何在任何时间点取消阻止在'theEvent'对象上调用'WaitOne'方法的所有线程?这个要求经常出现在我的设计中,因为我需要能够优雅地停止和启动我的多线程程序.在我看来,启动一个多线程程序相当简单,但很难阻止它.

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

public void UnblockAll()
{
    do
    {
        theEvent.Set();
    } while (theEvent.WaitOne(0));
}
Run Code Online (Sandbox Code Playgroud)

'UnblockAll'方法是'SomeClass'类的成员.此处使用的技术基于WaitOne方法的MSDN文档.我引用下面文档的相关部分:

如果millisecondsTimeout为零,则该方法不会阻止.它测试等待句柄的状态并立即返回.

在do..while循环中,我调用Set方法.这释放了一个可能由于调用WaitOne方法而被阻塞的线程(在'SomeMethod'方法中编码).接下来,我测试'theEvent'对象的状态,只是为了知道它是否有信号.此测试是通过调用带有超时参数的WaitOne方法的重载版本来完成的.我在调用WaitOne方法时使用的参数为零,根据文档结果,调用立即返回一个布尔值.如果返回值是true,那么"theEvent"的对象信号状态中.如果在'SomeMethod'方法中对'WaitOne'方法的调用至少阻塞了一个线程,则调用'Set'方法(在'UnblockAll'方法中编码)将解除阻塞.因此,在'UnblockAll'方法中do..while语句结束时对'WaitOne'方法的调用将返回false.仅当没有线程被阻止时,返回值才为真.

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

Bri*_*eon 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.
}
Run Code Online (Sandbox Code Playgroud)

选项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.
}
Run Code Online (Sandbox Code Playgroud)

选项3 - 使用 Thread.Interrupt

不要混淆这个Thread.Abort.中止线程绝对不安全,但中断线程完全不同.Thread.Interrupt将"戳"内建阻塞调用在BCL包括所用Thread.Join,WaitHandle.WaitOne,Thread.Sleep等.


Don*_*kby 3

您的例程可能在大多数情况下都有效,但我认为不能保证等待线程之一会在关闭循环设置事件的时间和关闭循环再次检查事件的时间之间重置事件。

我发现 AutoResetEvent 和 ManualResetEvent 类非常适合非常简单的场景。每当需求出现任何奇怪的情况时,我都会迅速切换到更灵活的等待和脉冲模式

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

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