Control.Invoke 在隐藏的 ShowDialog 中“卡住”

Ben*_*jol 5 .net c# multithreading invoke

(我有解决这个问题的方法,但这不是我第一次被咬,所以我试图确切地了解发生了什么。)

  • 从我的申请中,我得到了ShowDialog一张表格。
  • 表单上有一个按钮,单击该按钮时会调用另一个(非 GUI)线程上的代码。
  • 非 GUI 线程通过 Control.Invoke发回状态( Pushedthen )Released
  • 当表单看到 时Pushed,它会调用form.Hide()
  • 当表单看到 时Released,它会更改按钮的外观。

发生的情况是,有时(但并非每次)非 Gui 线程在尝试发送Released. 无一例外,Gui 继续“工作”,但无论在哪个方向,都不可能与非 Gui 线程进行进一步的通信。

线程的(简化的)调用堆栈如下所示:

System.Threading.WaitHandle.WaitOne()
(...)
System.Windows.Forms.Control.WaitForWaitHandle()
(...)
System.Windows.Forms.Control.Invoke()
(...)
GuiCode.OnStatusChanged()
(...)
NonGuiCode.SetStatus()
Run Code Online (Sandbox Code Playgroud)

ShowDialog如果我替换为,问题就会消失,但是 - 有趣的是 - 它会变得更好(发生的频率较低),但如果我注释掉执行onShow的代码,问题不会完全消失。HidePushed

更新

感谢nobugz,我发现了死锁(我以前只在数据库中遇到过它)!显然,用 Control.BeginInvoke 替换 Control.Invoke 可以解决此问题(状态事件有时仍然“卡住”,但它不会阻止所有后续通信)。

Han*_*ant 3

显然,您正在与僵局作斗争。当您使用 Control.Invoke() 而不是 BeginInvoke() 时,这种情况总是会发生。我不清楚是什么导致了你的帖子陷入僵局。一个危险信号是在使用 ShowDialog() 显示的窗体上使用 Hide()。这通常会关闭对话框。

最好的办法就是调试它。等到死锁发生,然后使用 Debug + Break All。使用 Debug + Windows + Threads 并切换到 UI 线程。查看调用堆栈。如果它没有泵送消息循环(Application.Run()),而是卡在某个地方(例如您似乎正在使用的等待句柄),那么就会导致死锁。