(我有一个解决这个问题的方法,但这不是我第一次被咬,所以我试图准确理解发生了什么。)
ShowDialog
表格。 Pushed
然后Released
)Pushed
时,会调用form.Hide()
Released
时,它会更改按钮的外观。有时,但不是每次,非Gui线程试图发送Released
时都会“卡住”。没有例外,Gui继续“工作”,但是在任何一个方向上都不可能与非Gui线程进行进一步的沟通。
线程的(简化)callstack如下所示:
System.Threading.WaitHandle.WaitOne()
(...)
System.Windows.Forms.Control.WaitForWaitHandle()
(...)
System.Windows.Forms.Control.Invoke()
(...)
GuiCode.OnStatusChanged()
(...)
NonGuiCode.SetStatus()
如果我用ShowDialog
替换Show
,问题就会消失,但是 - 有趣的是 - 它变得更好(发生的次数减少)但是如果我注释掉代码执行的话,它就不会完全消失Hide
上的Pushed
。
更新
感谢nobugz,我发现了死锁(我之前只在数据库中遇到过它)!显然用Control.BeginInvoke替换Control.Invoke解决了这个问题(状态事件有时会“卡住”,但它不会阻止所有后续通信。)
答案 0 :(得分:4)
要处理Control.Invoke()
调用,GUI线程必须提取Windows消息,但ShowDialog()
是阻塞调用,因此在ShowDialog()
返回之前不能执行此操作。
Control.Invoke()
也是阻塞的,调用它的线程必须等到GUI线程获取消息并处理它才能继续。如果包含Control.Invoke()
的代码能够解除对话,那么 bingo 就会导致您的死锁。
这有点棘手,因为像SosEx的dlk
命令这样的死锁检测器无法检测转储或WinDgb会话中的问题 - GUI线程处理Control.Invoke()
时涉及的“锁定”是“暗示”,而不是实际的WaitHandle
。
答案 1 :(得分:3)
最好的办法是调试它。等到发生死锁,然后使用Debug + Break All。使用Debug + Windows + Threads并切换到UI线程。查看调用堆栈。如果它没有引发消息循环(Application.Run())但是卡在某处(比如你似乎正在使用的等待句柄),那么结果就是死锁。
答案 2 :(得分:0)
我刚刚遇到了我的想法。
从GUI线程调用另一个GUI线程,让该线程执行ShowDialog。如果用户的GUI首选项发生变化(例如背景旋转器),则死锁。