簡體   English   中英

在異步方法上調用.Wait()和Task.Run()之間的區別.Wait()

[英]Difference between calling .Wait() on an async method, and Task.Run().Wait()

我在ASP.NET WebForms網站上有一個服務器端點擊事件。 在這種情況下,我調用一個方法,該方法又調用其異步伙伴方法,在調用時添加.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()

我在我的博客文章Do not Block on Asynchronous Code和我的MSDN關於異步最佳實踐的文章中解釋了這種行為。

線程似乎從兔子洞里消失了; 該方法永遠不會回電。

發生這種情況是因為其中一個await嘗試在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 eventhandler的簽名即可

這是正確的答案:不要阻止異步代碼。 只需使用async void事件處理程序並使用await代替。

PS ASP.NET Core不再具有ASP.NET上下文,因此您可以根據需要阻止,而不必擔心死鎖。 但是你當然不應該這樣做,因為它效率低下。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM