【问题标题】:Thread persists after application termination due to AutoResetEvent signal in WaitOne state由于 AutoResetEvent 信号处于 WaitOne 状态,线程在应用程序终止后仍然存在
【发布时间】:2011-07-08 22:23:25
【问题描述】:

我有一个应用程序在队列中使用 AutoResetEvent (WaitOne/Set) 来处理消息。我注意到当我从 Visual Studio (Shift+F5) 终止调试会话时,应用程序的原始进程会挂起(但并非总是如此)。我手动将调试器重新附加到进程,发现它有单个线程卡在 WaitHandle.WaitOne 上。

所以我的问题是,终止可能处于 WaitOne 状态的线程的正确方法是什么?

想到的第一个答案是听应用程序退出事件并在那里执行设置,但我不确定在这些调试会话之后是否可靠地调用了此事件,或者是否有我认为的更标准的做法我不知道。

作为第二个问题,对于在“生产”模式下运行的应用程序,您会以不同的方式处理这个问题吗?

【问题讨论】:

  • 嗯,你手上有一个讨厌的问题。终止调试会话是使用 Windows 中可用的最大工具 TerminateProcess() 完成的。 Taskmgr.exe 中使用的相同类型的枪。只有一种可能的方式不会终止进程,内核线程很忙并且没有完成 I/O 请求。那肯定不是 AutoResetEvent。没有意义。
  • @Hans - 所以我在附加调试器后在调用堆栈中看到的 WaitHandle 不是信号的等待句柄,而是内核 i/o 任务的不同等待句柄?此任务中唯一的 i/o 是一些日志记录,这非常简单(并且只是每隔一段时间手动触发一次,而不是任何频率发生的事情)
  • 我只知道你描述的没有意义。因此,如果没有任何文档,我很容易认为描述是错误的。在启用非托管调试和 Microsoft 符号服务器的情况下发布堆栈跟踪。
  • @Hans 感谢您的建议,当我回到办公室并准备好代码时会这样做。感谢您的指导。
  • 我刚刚意识到我确实有另一个 I/O 操作。我疲惫的大脑完全忘记了正在读取的 TcpStream,如果我没记错的话,当我瞥了一眼我正在使用的组件时,它会在流上对 Read 进行阻塞调用。有了这个认识,以及您对更高级调试的建议,我希望在周一解决问题

标签: c# .net-4.0 waithandle autoresetevent


【解决方案1】:

有一种简单的方法可以做到这一点(不是解决方法)

首先,您需要设置一个在您的应用程序即将终止时触发的事件

// somewhere with global scope. On a singleton or in program class maybe
// this is set when you want to terminate your application
private static ManualResetEvent ExitWaitHandle = new ManualResetEvent(false);

这是其他地方的使用方法

// the event you want to check but it's blocking your application termination
private static AutoResetEvent yourEvent = new AutoResetEvent(true);

// the method where you have the problem
private static void FooAsync()
{
    try
    {
        WaitHandle.WaitAny(new WaitHandle[]{yourEvent, ExitWaitHandle});
        Checkpoint();

        // other stuff here

        // check if thread must die
        Checkpoint();
    }
    catch(ApplicationTerminatingException)
    {
        // thread must die, do cleanup and finalization stuff here
    }
    catch(Exception)
    {
        // holy cow! what should we do?
    }
}

private void CheckPoint()
{
    // fast check if the exit handle is set
    if(ExitWaitHandle.WaitOne(0))
    {
        throw new ApplicationTerminatingException(); // custom exception
    }
}

唯一的开销是在“一些”代码之后,您需要设置一个检查点以中止您的线程。希望这就是您想要的。

【讨论】:

  • 这是一个非常有趣的答案。明天我将尝试这第一件事,让你知道它是如何进行的。谢谢。
  • 现在我正在再次查看这个问题,因为我面前有我的代码,并且有几个问题。 1)我认为您的“全球”范围 ExitWaitHandle 应该是公开的? 2)这里的基本思想是在关闭过程中通过 ExitWaitHandle 打开信号上的“门”,从而释放任何等待句柄并允许它们自行处理?
  • @Matt 是的,你打开了“门”!至于事件,它不应该是公开的,而是可以从所有需要监听应用程序终止事件的类中访问。您可以实现一个观察者模式,其中订阅者是线程。还有一点代码纳粹的评论:我更喜欢 internal 关键字而不是 public
  • 早期测试似乎表明这对我有用。感谢您对这个长期未解决的问题的帮助。
【解决方案2】:

一种解决方案是使用Thread.IsBackground 属性将线程设置为后台线程。在线程上设置时,该线程不会停止进程退出。

但是,线程可能随时中断,通常会导致未定义的行为,具体取决于您的线程正在执行的操作。以我的拙见,终止线程的最佳方法是发出线程退出的信号,例如通过设置退出标志并设置WaitHandle 并唤醒它然后Joining 线程。

【讨论】:

  • 线程实际上是通过 .NET 4 Task 对象处理的。我想我可以做一些手动穿线......
猜你喜欢
  • 2011-09-13
  • 2021-08-27
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 1970-01-01
  • 2018-07-01
相关资源
最近更新 更多