2016-11-25 72 views
1

我在ASP.NET WebForms網站上有一個服務器端單擊事件。在這個事件中,我調用一個方法,該方法又調用其異步合作伙伴方法,在呼叫中添加.Wait()。然後在異步方法調用.Wait()和Task.Run()之間的區別Wait()

該方法進入幾個層次向下(即,調用另一個異步方法,它調用另一個異步方法,等等),並最終調用一個HttpClient的對象上的異步方法。此時線程似乎消失在兔子洞下;該方法永遠不會回調。

現在,我知道異步系列調用按預期工作,因爲相同的代碼也是從Web API控制器調用的(控制器方法調用第一個方法的異步版本,而不是同步的「夥伴」方法) ,它完全異步,並且按預期返回。

所以基本上我有這樣的事情,它永遠不會返回

protected void btn_Click(object sender, EventArgs e) 
    > Class1.DoSomething() 
     > Class1.DoSomethingAsync.Wait() 
      ... 
       > await ClassN.Authenticate() 
        { 
        await myHttpClient.PostAsync() // never returns 
        } 

我也嘗試後第一個異步方法,但沒有任何成功使用.ConfigureAwait(false)

我也有這個,這確實返回

Task<IHttpActionResult> MyWebApiMethod() 
    > await Class1.DoSomethingAsync() 
     ... 
      > await ClassN.Authenticate() 
       { 
       await myHttpClient.PostAsync() // does return 
       } 

我發現我可以做的第一個版本的工作,如果我將其更改爲以下:

protected void btn_Click(object sender, EventArgs e) 
    > Class1.DoSomething() 
     > Task.Run(async() => await Class1.DoSomethingAsync()).Wait() 
      ... 
       > await ClassN.Authenticate() 
        { 
        await myHttpClient.PostAsync() 
        } 

但我不知道爲什麼。

誰能解釋調用

Class1.DoSomethingAsync.Wait() 

,並呼籲

Task.Run(async() => await Class1.DoSomethingAsync()).Wait() 
+3

爲什麼不以正確的方式使用它。 'async void btn_Click'和'await Class1.DoSomethingAsync()'? – user3185569

+1

不要對已有的異步方法使用'Task.Run',這是線程的浪費。只需更改'button_click'事件處理程序的簽名爲@ user3185569建議 – Fabio

+0

@ user3185569 - 我沒有使用過,因爲我不知道我可以在服務器端WebForms事件中使用'async'。謝謝你指出我。 – awj

回答

5

我在我的博客文章Don't Block on Asynchronous Codeasynchronous best practices我的MSDN文章解釋這種行爲之間的區別。

線程似乎消失在一個兔子洞;該方法永遠不會回調。

這是因爲await S的一個正試圖恢復在ASP.NET背景,但請求的線程(使用ASP.NET上下文)被阻塞等待任務完成。這是導致你的僵局的原因。

我曾嘗試在第一個異步方法上使用.ConfigureAwait(false),但沒有任何成功。

爲了避免使用ConfigureAwait(false)這個死鎖,它必須在每個方法稱爲被施加到每await。所以DoSomethingAsync必須每await使用它,DoSomethingAsync調用必須每await(例如,Authenticate)使用它的每種方法,那些方法調用的每個方法必須使用它每await(例如,PostAsync)等請注意,在這裏最後,你依賴於庫代碼,事實上HttpClient過去已經錯過了其中的一些。

我發現我可以使第一個版本工作,如果我改變它[使用Task.Run]。

是。 Task.Run將在線程池線程上執行其委託,而不會在任何上下文中執行委託。所以這就是爲什麼沒有死鎖:await都沒有嘗試在ASP.NET上下文中恢復。

爲什麼不以正確的方式使用它。 async void btn_Click並等待Class1.DoSomethingAsync()?

不要使用Task.Run與已經異步的方法,這是浪費線程。只要改變button_click事件處理程序的簽名

而這裏的正確回答:不異步代碼塊。只需使用async void事件處理程序並使用await代替。

P.S. ASP.NET Core不再具有ASP.NET上下文,因此您可以儘可能多地阻止死鎖。但是,你當然不應該這樣做,因爲它效率低下。

相關問題