[英]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.