2016-07-28 136 views
7

讓我前言這個問題,有幾件事情:混合異步/等待着結果

  1. 我讀過一些做題說你應該做到這一點(如How to safely mix sync and async code
  2. 我讀過Async/Await - Best Practices in Asynchronous Programming再次說你不應該這樣做

所以我不知道這是不是最好的做法,並且不需要任何人告訴我這樣的。這更像是「爲什麼這項工作」的問題。

有了這樣的方式,這裏是我的問題:

我已經寫了有2個按鈕和狀態標籤一個小的GUI應用程序。其中一個按鈕會在100%的時間內同步和異步重現死鎖問題。另一個按鈕調用相同的異步方法,但它被包裝在一個任務中,這個工作。我知道這不是一個好的編碼習慣,但我想了解爲什麼它不具有相同的死鎖問題。下面是代碼:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private async Task<string> DelayAsync() 
    { 
     await Task.Delay(1000); 
     return "Done"; 
    } 

    private void buttonDeadlock_Click(object sender, EventArgs e) 
    { 
     labelStatus.Text = "Status: Running"; 

     // causes a deadlock because of mixing sync and async code 
     var result = DelayAsync().Result; 
     // never gets here 
     labelStatus.Text = "Status: " + result; 
    } 

    private void buttonWorking_Click(object sender, EventArgs e) 
    { 
     labelStatus.Text = "Status: Running"; 
     string result = null; 

     // still technically mixes sync and async, but works, why? 
     result = Task.Run(async() => 
     { 
      return await DelayAsync(); 
     }).Result; 

     labelStatus.Text = "Status: " + result; 
    } 
} 

回答

11

它的工作原理,因爲buttonWorking_Click異步代碼(DelayAsync還有async拉姆達傳遞給Task.Run)不具有電流SynchronizationContext,而buttonDeadlock_Click異步代碼(DelayAsync)一樣。您可以通過在調試器中運行並觀察SynchronizationContext.Current來觀察差異。

我在我的博客文章Don't Block on Async Code中解釋了死鎖方案背後的細節。

+0

正是我想要的,謝謝你的快速答覆。 – Middas

9

情景一:你正坐在你的辦公桌前。有一個收件箱。它是空的。一張紙突然到達您的收件箱中描述一項任務。你跳到你的腳,開始跑來跑去做任務。但是什麼是任務?它說要做到以下幾點:

  • 更改白板說「運行」 - 好的,你這樣做。
  • 將鬧鐘設置一小時後。好的,你這樣做。
  • 創建一張新紙,上面寫着「當鬧鐘響起時,在白板上寫下完成的單詞」。把它放在收件箱裏。你做吧。
  • 直到DONE字已寫在白板上時才做其他事情。
  • 回到您的辦公桌,等待下一個任務到達收件箱。

此工作流程可防止您完成工作,因爲最後兩步的順序錯誤。

情景二:你正坐在你的辦公桌前。有一個收件箱。它是空的。一張紙突然到達您的收件箱中描述一項任務。你跳到你的腳,開始跑來跑去做任務。但是什麼是任務?它說要做到以下幾點:

  • 更改白板說「運行」 - 好的,你這樣做。
  • 把這張紙放在旁邊隔間的黛比。好的,你這樣做。
  • 在別人告訴你子任務已完成之前什麼也不做。
  • 發生這種情況時,請在白板上寫下DONE字樣。
  • 回到你的辦公桌。

你給黛比的那張紙說什麼?它說:

  • 將您的鬧鐘設置一個小時後。好的,她這樣做。
  • 當鬧鐘響起時,請在收件箱中放一張紙,告訴Middas您已完成。

此工作流程仍然是可怕(1)你坐在那裏什麼都不做,而你等待黛比的鬧鐘走下車,和(2)你是在浪費兩個工人的時候,你可能有一名工人做所有的工作。工人很貴。

但是,此工作流程並不妨礙您完成工作最終。它沒有死鎖,因爲你沒有在等待工作,你自己打算在將來做,你正在等待別人來做這項工作。

(我注意到,這不是什麼是你的程序發生了一個確切的比喻,但它是足夠接近整個的想法。)

+0

我很欣賞你的例子,它有助於把它變成一個更容易理解的上下文,爲什麼發生這種情況的死鎖。 – Middas

+3

這正是我訂閱Eric的StackOverflow RSS提要的原因。即使你知道答案,你一定會得到更多的見解和/或向同事解釋事情的好方法。 –