2016-08-16 322 views
1

我在寫關鍵區域的應用程序。AutoResetEvent.WaitOne()導致死鎖

我決定使用AutoResetEvent來實現互斥。 下面的代碼

public class MyViewModel 
    { 
     private AutoResetEvent lock = new AutoResetEvent(true); 
     private aync Task CriticalRegion() 
     { 
      Dosomething(); 
     } 


     public async Task Button_Click() 
     { 
      Debug.WriteLine("Entering Button_Click"); 
      lock.WaitOne(); 
      try 
      { 
       await CriticalRegion(); 
      } 
      finally 
      { 
       lock.Set(); 
       Debug.WriteLine("Leaving Button_Click"); 

      } 
     } 

    } 

我有一個按鈕,其單擊事件調用Button_Click()方法

它正常工作。但是,如果我在第一次致電Button_Click()完成之前再次點擊該按鈕的時間足夠快,則整個應用程序將停止響應。

在調試窗口,我覺得這樣的事情

Entering Button_Click 
Entering Button_Click 

貌似方法永遠不會完成。

我掙扎了一下,發現如果我改變lock.WaitOne();

if (!sync.WaitOne(TimeSpan.FromSeconds(1))) 
    { 
     return; 
    } 

在這種情況下,我的應用程序能夠避免僵局,但我不知道爲什麼它的工作原理。

我只知道我的操作系統課程中的IPC和C#中的asyncawait模式,我對.NET中的線程並不熟悉。

我真的很想了解幕後的真實情況。 感謝您的回信;),直到你調用AutoResetEvent.Set(),你似乎永遠只是WaitOne()呼叫做

+1

張貼的片段不足以證明僵局。但是,你做錯了是顯而易見的,關注類名。它是自動復位*事件*。事件用於發信號,「關鍵區域」需要互相排斥。這需要一個互斥鎖,最容易在C#中用'lock'關鍵字完成。在UI線程上使用鎖定在形式上是非法的,實際上很可能導致死鎖。 –

回答

4

你有一個僵局,因爲WaitOne阻止主線程(按一下按鈕處理程序是在主線程上執行),同時呼籲await當你已經不叫ConfigureAwait(false),這意味着它試圖運行代碼在主線程上是await之後,即使它被阻塞,這也會導致死鎖。

我建議您閱讀this post,詳細解釋死鎖情況。

爲您的代碼,我建議把鎖越深,大概是異步任務中,並嘗試使用鎖定,優選地,lock statement更合適的模式,因爲使用Event對象是尷尬的相互排斥,因爲漢斯在評論中表示。

+0

謝謝,這些帖子真的幫了很多。另一個問題,我的關鍵區域實際上包含一些'await'關鍵字。那麼使用'SemaphoreSlim.WaitAsync()'怎麼樣? – Hohenheim

+0

看來'SemaphoreSlim'確實是一個優選的方法,具有異步鎖定功能。 – argaz

0

AutoResetEvent.WaitOne()將阻止無限。

引述AutoResetEvent.WaitOne()文檔:

阻塞當前線程,直到當前的WaitHandle接收信號

+0

你是對的,但是他描述的是一個更多的種族條件僵局,而不是100%,所以我認爲我的解釋在這種情況下更合適。 – argaz