2010-01-13 94 views
4

(我對這個問題有個解決方法,但這不是我第一次被咬,所以我想知道到底發生了什麼)Control.Invoke在隱藏的ShowDialog中被卡住

  • 從我的申請,我ShowDialog的一種形式。
  • 在表單上是一個按鈕,當點擊按鈕時調用另一個(非Gui)線程上的代碼。
  • 非GUI線程通過發送回狀態(Pushed然後Released)一個Control.Invoke
  • 當窗體看到Pushed,它調用form.Hide()
  • 當窗體看到Released,它改變的外觀按鈕。

會發生什麼情況是,有時,但不是每次,非Gui線程都會'卡住'試圖發送Released。沒有例外,桂繼續「工作」,但在任何方向上都不可能與非桂線進行進一步的溝通。

(簡化)調用堆棧的線程看起來是這樣的:

System.Threading.WaitHandle.WaitOne() 
(...) 
System.Windows.Forms.Control.WaitForWaitHandle() 
(...) 
System.Windows.Forms.Control.Invoke() 
(...) 
GuiCode.OnStatusChanged() 
(...) 
NonGuiCode.SetStatus() 

的問題消失,如果我有Show取代ShowDialog,但 - 有趣的是 - 它變得更好(經常發生較少),但不如果我註釋掉Pushed上的Hide的代碼,則完全消失。

更新

感謝nobugz,我發現死鎖(我只見過它在數據庫之前)!顯然用Control.BeginInvoke替代Control.Invoke可以解決這個問題(狀態事件有時會「卡住」,但不會阻塞所有後續通信)。

回答

3

顯然,你正在與僵局作鬥爭。當您使用Control.Invoke()而不是BeginInvoke()時,這總是在角落。我不清楚你的帖子中發生了什麼僵局。一個紅旗正在使用ShowDialog()顯示的窗體上使用Hide()。通常會關閉對話框。

最好的事情就是調試它。等到死鎖發生,然後使用Debug + Break All。使用Debug + Windows +線程並切換到UI線程。看看Call Stack。如果它不是抽取消息循環(Application.Run()),而是停留在某處(如您似乎正在使用的等待句柄),則會導致死鎖。

+0

是的,我試圖回收表單。話雖如此,關閉它,或只是設置DialogResult,仍然具有相同的效果。現在我需要去了解Invoke和BeginInvoke之間的區別... – Benjol 2010-01-13 13:12:40

0

我剛剛遇到了我認爲是這樣的。

從GUI線程調用到另一個GUI線程,並讓該線程執行ShowDialog。如果用戶的GUI偏好改變(例如背景旋轉),死鎖。

4

要處理Control.Invoke()調用,GUI線程必須抽取Windows消息,但ShowDialog()是阻塞呼叫,所以直到ShowDialog()返回時才能這樣做。

Control.Invoke()也是阻塞的,調用它的線程必須等到GUI線程拾取消息並處理它才能繼續。如果包含Control.Invoke()的代碼是允許解除對話框的代碼,那麼您的僵局就是這樣。

這是所有有點棘手,因爲僵局探測器像SosEx的dlk命令不能從轉儲或WinDgb會話檢測問題 - 「鎖定」參與Control.Invoke() GUI線程的處理是「暗示」,而不是實際的WaitHandle