2012-07-19 77 views
5

我正在使用定期從工作線程發出事件的外部組件。在我的事件處理程序中,我使用Dispatcher來調用主線程上的某個方法。這很好地工作......當工作線程嘗試調用主線程上的某些東西時發生死鎖

private void HandleXYZ(object sender, EventArgs e) 
{ 
    ... 
    if(OnTrigger != null) 
     dispatcher.Invoke(OnTrigger, new TimeSpan(0, 0, 1), e); 
} 

然而,當程序關閉和外部組件的Dispose()S,節目有時掛起(也只能看到在任務管理器殺死)。

當我看看發生了什麼時,它看起來像「組件」正在等待事件在主線程上返回(它停留在Dispose()方法中),而工作線程等待調度程序調用上述對主線程的調用(它掛在調度程序調用行中)。

現在我通過給Invoke添加一個超時來解決shutdown問題,這似乎工作但感覺不對。 有沒有更乾淨的方法來做這樣的事情?我可以強制主線程在關閉之前花費一些時間從其他線程處理任務嗎?

我曾經試圖「斷開」關停前的事件,但沒有幫助,因爲調度程序(可能是)已經在等待,當程序開始關閉...

PS:外部組件意味着我無法訪問源代碼...

+0

請使用段落下一次 – Shai 2012-07-19 09:38:47

+1

粘貼代碼將有很大的幫助 – Vedran 2012-07-19 09:40:58

回答

6

是的,這是死鎖的常見原因。它掛起是因爲調度程序退出調度程序循環,它不再響應調用請求。快速解決方法是使用BeginInvoke,而不是等待調用目標完成執行。另一個快捷方式是將工作線程的IsBackground屬性設置爲True,以便CLR將其殺死。

這些都是快速修復,他們可能會爲你工作。當然在你的開發機器上,但如果你有一種嘮叨的感覺,它可能仍然出錯,那麼你是對的,沒有觀察到死鎖或線程競爭而不是證明它們不存在。有兩個「好」的方式做到這一點完全安全:

  • 不允許主線程退出,直到你肯定的工作線程終止,不能再引發事件。 This answer顯示該模式。

  • 用Environment.Exit()強制終止程序。這是非常原油,但有效的,只有當你有一個嚴重的線程程序,其中的UI線程只有第二公民時,才能達到的大錘。奇怪的是,這聽起來可能是一種合適的方法,新的C++語言標準已將其提升爲支持終止程序的方式。你可以在this answer瞭解更多關於它的信息。注意它是如何允許註冊清理函數的,你必須做一些類似的事情,比如說,AppDomain.ProcessExit事件。在你做這件事之前,請關注第一顆子彈。

+0

很好的答案!我會說BeginInvoke方式感覺比暫停更好。 – FrankB 2012-07-19 10:38:11

+0

這是否意味着:如果程序沒有關閉,並且主線程將等待事件...調度程序是否將調用通過主線程,儘管主線程正在「主動」等待? – FrankB 2012-07-19 10:54:25

+0

不知道這是如何與原來的問題有關。但主線程中的任何「等待」都可能導致死鎖。只有在主線程處於空閒狀態並執行調度程序循環時才能調用Invoke()調用。 – 2012-07-19 11:04:34

1

至於事件訂閱,當您知道不再需要某個特定對象時,清理它們確實是一個不錯的主意。否則,你會冒險造成內存泄漏。您可能還想看看weak event pattern(MSDN)。

關於死鎖本身,不知道你的代碼,我們只能猜測。

我沒有看到HandleXYZ()作爲罪魁禍首,我寧願檢查您的IDisposable()實施。看看MSDN documentation並將其與您的實施進行比較。

我想在你的實現中有一些方法調用取決於GarbageCollector的時間,這是不確定的:有時它可能會在你的情況下有效,有時它可能不會。

+0

謝謝...但不幸的Dispose()的代碼是不提供給我(外部組件) – FrankB 2012-07-19 10:07:41

+1

的問題是,@FrankB,你做清理嗎?作爲一般規則,總是清理任何實現'IDispoeable()'的類。如果該第三方組件正在等待某些內容,則很可能出現這種情況,即您自己的代碼沒有正確釋放某些資源。 – 2012-07-19 10:13:58

+0

其他組件等待事件返回,而我的事件處理程序等待主線程(它被等待組件阻止...)。如果我強制Dispatcher停止等待主線程,清理只是解決方案。除了設置超時之外,我可以這麼做嗎? – FrankB 2012-07-19 10:20:42