当工作线程试图在主线程上调用某些东西时死锁

时间:2012-07-19 09:35:39

标签: c# multithreading deadlock

我正在使用一个外部组件,它定期从工作线程中射出事件。在我的事件处理程序中,我使用Dispatcher在主线程上调用某些方法。这很好用......

private void HandleXYZ(object sender, EventArgs e)
{
    ...
    if(OnTrigger != null)
        dispatcher.Invoke(OnTrigger, new TimeSpan(0, 0, 1), e);
}

但是,当程序关闭并且外部组件Dispose()时,程序有时挂起(并且只能在任务管理器中查看和终止)。

当我看到正在发生的事情时,看起来“组件”正在等待事件在主线程上返回(它保留在Dispose()方法中),而工作线程等待调度程序调用提到对主线程的调用(它挂在dispatcher.Invoke-line中)。

现在我通过在Invoke中添加一个超时解决了关机问题,这似乎有效,但感觉不对。 有没有更清洁的方法来做这样的事情?在关闭之前,我可以强制主线程花一些时间从其他线程获取作业吗?

我试图在关闭之前“断开”事件,但这没有用,因为调度程序(可能)已经在等待,当程序开始关闭时......

PS:外部组件在这里意味着我无权访问源代码......

2 个答案:

答案 0 :(得分:6)

是的,这是死锁的常见原因。它挂起,因为调度程序退出调度程序循环,它不再响应Invoke请求。快速解决方法是使用BeginInvoke,它不会等待调用目标完成执行。另一个简单的方法是将工作线程的IsBackground属性设置为True,以便CLR将其杀死。

这些是快速修复,它们可能适合您。当然在你的开发机器上,但如果你有一种唠叨的感觉,它可能仍然出错,那么你是对的,没有观察到死锁或线程竞争证明它们不存在。有两种“好”的方法可以完全安全地完成它:

  • 确定工作线程终止并且不再引发事件之前,不允许主线程退出。 This answer显示了模式。

  • 使用Environment.Exit()强制终止程序。这是非常粗糙,但是非常有效,当你有一个高度线程化的程序,其中UI线程只是第二个公民时,你只能达到这个大锤。虽然这可能听起来像是一种合适的方法,但新的C ++语言标准已将其提升为支持终止程序的方式。您可以在this answer中详细了解相关信息。请注意它如何允许注册清理功能,您必须执行与AppDomain.ProcessExit事件类似的操作。在你这样做之前,先关注第一颗子弹。

答案 1 :(得分:1)

对于事件订阅,当您知道不再需要特定对象时,确实清理它们确实是个好主意。否则,您将冒险创建内存泄漏。您可能还想查看weak event pattern(MSDN)。

关于死锁本身,在不知道你的代码的情况下,我们只能猜测。

我不认为HandleXYZ()是罪魁祸首,我宁愿检查你的IDisposable()执行情况。查看MSDN documentation并将其与您的实施进行比较。

我认为在你的实现中的某个地方有一些方法调用取决于GarbageCollector的时间,这是不确定的:有时它可能在你的情况下有效,有时它可能没有。