C# 工作线程试图调用主线程上的某个对象时发生死锁

C# 工作线程试图调用主线程上的某个对象时发生死锁,c#,multithreading,deadlock,C#,Multithreading,Deadlock,我使用的是一个外部组件,它定期从工作线程中触发事件。在我的事件处理程序中,我使用一个调度程序来调用主线程上的某个方法。这个很好用 private void HandleXYZ(object sender, EventArgs e) { ... if(OnTrigger != null) dispatcher.Invoke(OnTrigger, new TimeSpan(0, 0, 1), e); } 但是,当程序关闭且外部组件Dispose()关闭时,程序有时会

我使用的是一个外部组件,它定期从工作线程中触发事件。在我的事件处理程序中,我使用一个调度程序来调用主线程上的某个方法。这个很好用

private void HandleXYZ(object sender, EventArgs e)
{
    ...
    if(OnTrigger != null)
        dispatcher.Invoke(OnTrigger, new TimeSpan(0, 0, 1), e);
}
但是,当程序关闭且外部组件Dispose()关闭时,程序有时会挂起(并且只能在任务管理器中看到并终止)

当我观察正在发生的事情时,看起来“组件”正在等待事件在主线程上返回(它停留在Dispose()方法中),而工作线程则在等待调度程序调用所提到的对主线程的调用(它挂在dispatcher.invoke行中)

目前,我通过向调用添加一个超时来解决关机问题,这似乎可行,但感觉不对。 有没有更干净的方法来做这样的事情?在关闭之前,我可以强制主线程花一些时间处理来自其他线程的作业吗

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


PS:外部组件在这里意味着我没有访问源代码的权限…

至于事件订阅,当您知道不再需要特定对象时,清除它们确实是一个好主意。否则,您将有造成内存泄漏的风险。您可能还想看看(MSDN)

关于死锁本身,在不了解代码的情况下,我们只能猜测

我不认为
HandleXYZ()
是罪魁祸首,我宁愿检查您的
IDisposable()
实现。请查看并将其与您的实现进行比较


我假设在您的实现中的某个地方进行了一些方法调用,这些调用取决于
GarbageCollector
的计时,这是不确定的:在您的情况下,有时可能会成功,有时可能不会。

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

这些都是快速修复方法,它们很可能对您有效。当然是在您的开发机器上,但是如果您有一种唠叨的感觉,认为它仍然可能出错,那么您是对的,没有观察到死锁或线程竞赛并不能证明它们不存在。有两种“好”方法可以完全安全地执行此操作:

  • 在确定工作线程已终止且无法再引发事件之前,不要允许主线程退出。显示模式

  • 使用Environment.Exit()强制终止程序。这是一个非常粗糙但非常有效的大锤,只有在UI线程只是第二个公民的重线程程序中,您才能使用它。奇怪的是,这可能听起来是一种合适的方法,新的C++语言标准已经将它提升为支持终止程序的方式。您可以在中阅读更多关于它的信息。请注意它是如何允许注册清理函数的,您必须对AppDomain.ProcessExit事件执行类似的操作。在做这件事之前,先把注意力集中在第一颗子弹上


下次请使用段落粘贴代码会有很大帮助。。。但不幸的是,Dispose()的代码对我(外部组件)不可用。问题是,@FrankB,你做过清理吗?作为一般规则,始终清理实现
idispoable()
的任何类。如果第三方组件正在等待某些内容,很可能是您自己的代码没有正确释放某些资源。另一个组件等待事件返回,而我的事件处理程序等待主线程(被等待的组件阻止…)。只有当我可以强制调度程序停止等待主线程时,清理才是解决方案。除了设置一个超时时间,我还能这样做吗?回答得好!我认为BeginInvoke方式感觉比超时好。这是否意味着:如果程序没有关闭,主线程将等待事件。。。尽管主线程正在“主动”等待,调度程序是否会将调用传递到主线程?不确定这与原始问题有何关系。但主线程中的任何“等待”都可能导致死锁。只有在主线程空闲并执行dispatcher循环时,才能调度Invoke()调用。谢谢!(这个问题主要与你的回答有关……但你的回答告诉我,关闭不一定在僵局中起作用——这是很好的了解)我使用大锤已经30年了。在确定终止工作线程之前不允许主线程退出通常是困难的、不切实际的或实际上不可能的。”新的C++语言标准将它提升为一种支持程序终止程序的方法,这是因为它是唯一可行的方法-只有OS有工具在任何内核上的任何状态下终止线程。除非我必须这样做,否则我不会使用像Invoke()这样的同步命令,也不会在appclose上等待任何可能阻止调用ExitProcess()的操作。