C# 控件。调用获取';卡住';在隐藏的ShowDialog中
(我有一个解决这个问题的方法,但这不是我第一次被咬,所以我正试图确切地了解到底发生了什么。)C# 控件。调用获取';卡住';在隐藏的ShowDialog中,c#,.net,multithreading,invoke,C#,.net,Multithreading,Invoke,(我有一个解决这个问题的方法,但这不是我第一次被咬,所以我正试图确切地了解到底发生了什么。) 从我的应用程序中,我ShowDialoga表单 表单上有一个按钮,单击该按钮可调用另一个(非Gui)线程上的代码 非GUI线程通过控件返回状态(push然后Released) 当表单看到推送的时,它调用表单.Hide() 当表单看到发布的时,它会更改按钮的外观 发生的情况是,有时,但不是每次,非Gui线程在试图发送发布的时“卡住”。没有例外,Gui将继续“工作”,但不可能在任何方向与非Gui线程进行
- 从我的应用程序中,我
a表单李>ShowDialog
- 表单上有一个按钮,单击该按钮可调用另一个(非Gui)线程上的代码
- 非GUI线程通过控件返回状态(
然后push
)Released
- 当表单看到推送的
时,它调用
表单.Hide()
- 当表单看到发布的
时,它会更改按钮的外观
时“卡住”。没有例外,Gui将继续“工作”,但不可能在任何方向与非Gui线程进行进一步通信
线程的(简化的)调用堆栈如下所示:
System.Threading.WaitHandle.WaitOne()
(...)
System.Windows.Forms.Control.WaitForWaitHandle()
(...)
System.Windows.Forms.Control.Invoke()
(...)
GuiCode.OnStatusChanged()
(...)
NonGuiCode.SetStatus()
如果我将ShowDialog
替换为Show
,问题就会消失,但有趣的是,它会变得更好(发生的次数更少),但如果我注释掉在上执行隐藏的代码,问题就不会完全消失
更新
多亏了,我发现了死锁(我以前只在数据库中遇到过它)!显然,将Control.Invoke替换为Control.BeginInvoke可以解决此问题(有时状态事件仍然会“卡住”,但它不会阻止所有后续通信)。显然,您正在与死锁作斗争。当您使用Control.Invoke()而不是BeginInvoke()时,这一点总是很容易实现的。我不清楚是什么打破了你帖子中的僵局。一个红色标志是在使用ShowDialog()显示的窗体上使用Hide()。这通常会关闭对话框
最好的办法是调试它。等待死锁发生,然后使用Debug+Break All。使用Debug+Windows+线程并切换到UI线程。看看调用堆栈。如果它不是在泵送消息循环(Application.Run()),而是被卡在某个地方(如您似乎正在使用的等待句柄),则会导致死锁。我刚刚遇到了我认为是这样的情况
从一个GUI线程调用另一个GUI线程,并让该线程执行ShowDialog。如果用户的GUI首选项发生更改(例如后台旋转器),则会死锁。要处理控件.Invoke()
调用,GUI线程必须发送Windows消息,但是ShowDialog()
是一个阻塞调用,因此在ShowDialog()
返回之前,它无法执行此操作
Control.Invoke()
也在阻塞,调用它的线程必须等待GUI线程拾取消息并处理它才能继续。如果包含控件.Invoke()
的代码允许取消对话框,那么宾果操作,就会出现死锁
这有点棘手,因为像SosEx的dlk
命令这样的死锁检测器无法从转储或WinDgb会话中检测问题-GUI线程处理Control.Invoke()
所涉及的“锁”是“隐含的”,而不是实际的WaitHandle
是的,我正在尝试回收表单。尽管如此,关闭它,或者只是设置DialogResult,仍然具有相同的效果。现在我需要去读一下Invoke和BeginInvoke之间的区别。。。