2012-03-27 30 views
3

這是怎麼回事?從委託內鎖定靜態對象不起作用

private static object Lock_HandleError = new object(); 
public static void HandleError(Exception ex) 
{ 
    lock(Lock_HandleError) 
    { 
     //IF the UI is processing a visual tree event (such as IsVisibleChanged), it throws an exception when showing a MessageBox as described here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/44962927-006e-4629-9aa3-100357861442 
     //The solution is to dispatch and queue the MessageBox. We must use BeginInvoke because dispatcher processing is suspended in such cases. 
     Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate() 
     { 
      lock(Lock_HandleError) 
      { 
       Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate(){ 
        HandleError(new Exception("testing purposes only")); 
       }, DispatcherPriority.Background); 

       MessageBox.Show(ex.Message, "Application Error", MessageBoxButton.OK, MessageBoxImage.Error); 
       //This point is not reached until the user clicks "OK" 
      } 
     }, DispatcherPriority.Background); 
    } 
} 

public void main() 
{ 
    HandleError(new Exception("The first error")); 
} 

上述代碼的預期的行爲是一個錯誤信息會出現在一個時間,當用戶點擊「確定」後,在Lock_HandleError對象將得到派出線程釋放,下一調用HandleError可以繼續 - 但是我得到的是無限級錯誤消息,沒有打到「OK」。

爲什麼這個鎖不工作?

通過在每個鎖定語句的入口和出口處設置斷點,我可以清楚地看到代理正在調用lock(),並再次調用一個新的「HandleError」調用,然後在MessageBox上暫停以等待用戶輸入。

與此同時,在另一個線程中,調度HandleError的調用得到運行 - 而不是像它應該那樣在lock()語句中等待,即使MessageBox委託清楚地放置了一個鎖並且還沒有發佈它。

+0

我對我的多線程不太熟悉,但是我想知道是否可以嘗試在'BeginInvoke'方法中刪除'lock'? – Richard 2012-03-27 16:07:54

+0

刪除鎖定並不能完成任何操作 - 因爲鎖定就像現在不存在一樣。雖然UI線程正在等待用戶在消息框中單擊「確定」,但它會處理來自應用程序其他區域的HandleError()的額外調用,並且我需要它停止並等待。 – Alain 2012-03-27 16:34:50

+0

我試圖在這裏使用信號量來解決問題:http://stackoverflow.com/questions/9894750/how-can-i-get-the-ui-thread-to-wait-on-a-semaphore-but -process-additional-dispa – Alain 2012-03-27 17:48:54

回答

6

兩個部分答案:

  1. 明白,鎖重入的。當一個線程已經在一個對象上持有一個鎖時,該線程可以一次又一次地使用同一個鎖而不會阻塞。

  2. 第一個MessageBox啓動時,UI線程仍在泵送消息,因此在UI線程上正在處理對HandleError的後續(遞歸)調用(因爲它已經保存了鎖,可以重新輸入它)。

+0

到目前爲止,這是最完整的解釋,因爲我都希望MessageBox能夠阻止它運行的線程(根據'2.'它沒有),我期待鎖可以像信號量一樣阻塞,不管哪個線程擁有它(根據'1.'它不)。雖然我的問題沒有明確要求解決我的問題的方法,但爲什麼我的方法不起作用,我仍然有興趣知道是否有解決這類問題的簡單方法。 – Alain 2012-03-27 16:38:26

6

爲什麼這個鎖不工作?

線程被允許輸入它已經擁有的鎖定語句。在本質上,鎖不會阻塞自己的線程。

因此,發生了什麼事是原始線程需要鎖,然後才允許將消息添加到分派器的隊列中。它可以添加儘可能多的想要的。

Dispatcher在處理時獲取第一條消息,然後調用HandleError。由於這是在調度程序線程中運行的,因此它可以進入外部和內部鎖定,並再次調用HandleError,以無限循環的方式遞增地向隊列中添加新消息。

+0

感謝您的解釋。顯然,lock()不是我想要使用的。我怎樣才能達到我的目標? – Alain 2012-03-27 16:10:25

+0

@Alain你爲什麼用BeginInvoke遞歸調用自己?這將是有問題的,因爲任何給予你以後的行爲的東西都會導致你死鎖。 – 2012-03-27 16:17:42

+0

這是模擬實際發生的事情,這是許多不同的線程遇到同時發生的錯誤,並且它們都會在UI線程上調用'HandleError()',而不是等待一個MessageBox在處理下一個錯誤之前被解散,應用程序會同時級聯所有錯誤。如果我們有一個反覆發生的錯誤,這個鎖不起作用,使我無法檢測和處理無限級別的消息框。 – Alain 2012-03-27 16:23:02