4

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

  • 从我的申请中,我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()

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

更新

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

4

3 回答 3

4

为了处理Control.Invoke()调用,GUI 线程必须发送一条 Windows 消息,但它是一个阻塞调用,因此在返回ShowDialog()之前它不能这样做。ShowDialog()

Control.Invoke()也是阻塞的,调用它的线程必须等到 GUI 线程接收到消息并处理它才能继续。如果包含 的代码Control.Invoke()可以解除对话框,那么bingo就会出现死锁。

这有点棘手,因为像 SosEx 的dlk命令这样的死锁检测器无法从转储或 WinDgb 会话中检测到问题 - GUI 线程处理中涉及的“锁定”Control.Invoke()是“隐含的”,而不是实际的WaitHandle.

于 2010-07-29T19:24:39.983 回答
3

显然,您正在与僵局作斗争。当您使用 Control.Invoke() 而不是 BeginInvoke() 时,这总是指日可待。我不清楚是什么使您的帖子陷入僵局。一个危险信号是在使用 ShowDialog() 显示的表单上使用 Hide()。这通常会关闭对话框。

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

于 2010-01-13T12:05:20.150 回答
0

我只是遇到了我认为是这样的。

从一个 GUI 线程调用另一个 GUI 线程,并让该线程执行 ShowDialog。如果用户的 GUI 偏好改变(例如背景旋转器),死锁。

于 2010-02-16T19:00:43.597 回答