【问题标题】:How do you close an application when some WaitHandle is in the middle of a call to WaitOne?当某个 WaitHandle 正在调用 WaitOne 时,如何关闭应用程序?
【发布时间】:2010-06-21 19:11:14
【问题描述】:

当某些WaitHandle 对象可能处于对WaitOne 的当前阻塞调用状态时,是否有一种标准方法可以“干净地”关闭应用程序?

例如,可能有一个后台线程在这样的方法中旋转:

while (_request.WaitOne())
{
    try
    {
        _workItem.Invoke();
    }
    finally
    {
        OnWorkCompleted();
    }
}

如果不调用Thread.Abort,我看不出有明显的方法来处理这个线程(据我所知,这是不鼓励的)。但是,在 _request 对象(AutoResetEvent)上调用 Close 会引发异常。

目前,运行此循环的线程将其IsBackground 属性设置为true,因此应用程序似乎 可以正常关闭。但是,由于 WaitHandle 实现了 IDisposable,我不确定这是否被认为是 kosher,或者该对象是否真的应该在应用退出之前被处置。

这是一个糟糕的设计吗?如果不是,通常如何处理这种情况?

【问题讨论】:

    标签: c# multithreading idisposable shutdown waithandle


    【解决方案1】:

    定义一个名为_terminate 的附加WaitHandle,它将发出终止循环的请求,然后使用WaitHandle.WaitAny 而不是WaitHandle.WaitOne

    var handles = { _request, _terminate };
    while (WaitHandle.WaitAny(handles) == 0)
    {
      try
      {
        _workItem.Invoke();
      }
      finally
      {
        OnCompleteWork();
      }
    }
    

    【讨论】:

    • 完全有道理。但这是否意味着可以简单地使用IsBackground = true 初始化运行此循环的线程(这样当应用程序退出时,即使WaitHandle 仍在等待,循环也会终止)?
    • @Dan:这也可以,但是这种方法可以让你优雅地结束线程,而不必关闭整个应用程序。
    • 嗨。我刚刚问过你关于这个模式的另一个问题,这是通过等待句柄停止线程的模式吗? (而不是使用中止/中断)?
    • @RoyiNamir:或多或少……是的。通常只会创建一个ManualResetEvent,然后使用超时参数调用WaitOne,以模拟无限循环中的轮询间隔。如果WaitOne 返回false 然后循环继续旋转。如果它返回true(因为Set被调用来请求关闭)然后循环停止。
    • Brian 谢谢,如果可以的话,还有 1 个问题:你为什么说 ManualResetEvent?如果有一个线程正在等待 - AutoResetEvent 应该很好并且更适合此调用(因为我只有一个线程在等待,我不希望所有等待的线程都收到信号)。我说的对吗?
    【解决方案2】:

    当一个线程阻塞时(不管它阻塞在什么上面)你可以调用Thread.Interrupt()这将导致异常ThreadInterruptedException(我相信它可能有点不同)你可以在线程本身上处理这个异常并进行任何必要的清理。

    值得注意的是,线程只会在阻塞时抛出ThreadInterruptedException,如果它没有阻塞,直到下一次尝试阻塞时才会抛出。

    这是从我读过的主题中结束线程的“安全”方式。

    还值得注意的是:如果对象同时实现了 IDisposable 和终结器(如果它使用非托管资源,它将实现),GC 将调用通常调用 dispose 的终结器。通常这是不确定的。但是,您几乎可以保证它们会在应用程序退出时被调用。只有在非常特殊的情况下他们才不会。 (抛出.net 环境终止异常,如StackOverflowException

    【讨论】:

      【解决方案3】:

      IsBackground 属性设置为true...它应该会在您的应用结束时自动关闭线程。

      或者,您可以通过调用Thread.Interrupt 来中断线程并处理ThreadInterruptedException。另一个想法是调用 _request.Set() 并让 while 循环检查一个 volatile 标志以确定应用程序是否正在关闭或是否应该继续:

      private volatile bool _running = true;
      while(_request.WaitOne() && _running)
      {
          //...
      }
      
      // somewhere else in the app
      _running = false;
      _request.Set();
      

      【讨论】:

      • 所以你是说只要线程是后台线程就不需要在WaitHandle上调用Close
      • @Dan,它对我来说总是正确退出......我正在寻找文档中的内容来支持该声明。
      • 这与 Thread.Abort() 完全相同。只是CLR会调用它。
      【解决方案4】:

      我认为操作系统会在您的进程完成后进行清理。因为你的线程被标记为 IsBackground,CLR 将结束进程和其中的所有线程,所以这不是问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-18
        • 2012-02-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多