![](/img/trans.png)
[英]If i add async keyword to a method which which should return a Task it wrap the return value in Task?
[英]Should I add the async modifier if I return a Task in an expensive method?
如果我們有一個具有async
修飾符但不使用await
運算符的方法,則C#編譯器已經警告我們。
根據這個答案,沒有必要在異步方法的末尾添加一個await
(在這種情況下,只需刪除async
修飾符即可)。
但是,如果該方法需要執行昂貴的同步操作,然后再調用后續的真正異步方法,該怎么辦?
例如,如果我使用HttpClient
:
private readonly HttpClient client = ...
public Task<HttpResponseMessage> CallMyWebServiceMethod() {
HttpRequestMessage request = this.SomeExpensiveButSynchronousMethod();
return this.client.SendAsync( request );
}
此代碼將阻止調用方(由於SomeExpensiveButSynchronousMethod
)。
但是,如果我將代碼更改為此:
public async Task<HttpResponseMessage> CallMyWebServiceMethod() {
HttpRequestMessage request = this.SomeExpensiveButSynchronousMethod();
return await this.client.SendAsync( request );
}
我這樣稱呼它:
HttpResponse response = await myWrapper.CallMyWebServiceMethod();
...我知道TPL會立即啟動后台線程,然后在后台線程中運行CallMyWebServiceMethod
,恢復父代碼想要的任何內容,使整個調用在該過程中成為非阻塞狀態,然后在Task完成並返回HttpResponse
。
...如果是這種情況,那似乎是矛盾的。
如果我錯了,並且調用一直阻塞,直到到達SendAsync
那么如何在與HttpClient
用於其請求的同一后台線程上執行SomeExpensiveButSynchronousMethod
?
根據這個答案,沒有必要在異步方法的末尾添加一個await(在這種情況下,只需刪除async修飾符即可)。
這太簡單了。 參見我關於消除異步和等待的博客文章。
但是,如果該方法需要執行昂貴的同步操作,然后再調用后續的真正異步方法,該怎么辦?
這種情況很少見,但是IMO的適當解決方案是同步執行同步操作(即,不包裝在Task.Run
),並確保記錄其行為 。
我知道TPL會立即啟動后台線程,然后在后台線程中運行CallMyWebServiceMethod,恢復父代碼想要的任何內容,使整個調用在該過程中成為非阻塞狀態,然后在Task完成並返回HttpResponse之后繼續執行。
那根本不會發生什么。 您可能會發現我的async
介紹很有幫助。 引用:
異步方法的開始與其他方法一樣執行。 也就是說,它會同步運行,直到達到“ await”(或引發異常)為止。
實際上,您的兩個示例在執行SomeExpensiveButSynchronousMethod
都同步阻塞了調用者。
如果我錯了,並且調用一直阻塞,直到到達SendAsync,那么如何在與HttpClient用於其請求的同一后台線程上執行SomeExpensiveButSynchronousMethod?
HttpClient
的請求不使用后台線程,因此問題的這一部分沒有意義。 有關異步I / O如何工作的更多信息,請參見我的博客文章There No Thread 。
要回答實際問題:
如果我以昂貴的方法返回Task,是否應該添加async修飾符?
是。 但是,您這樣做的原因不是“使其異步”; 這樣一來,將捕獲SomeExpensiveButSynchronousMethod
中的所有異常並將其放置在返回的Task
,這是遵循基於任務的異步模式(TAP)的方法的預期語義。
我知道TPL會立即啟動后台線程,然后運行
CallMyWebServiceMethod
不,那不會發生。 async
方法的第一個同步部分是async
執行的。 如果要確保它不會阻塞當前線程,則應使用Task.Run()
。
您昂貴的方法將以兩種方式阻止調用者。 async
不會神奇地為您創建線程,這就是Task
所做的(有時)。 因此,如果添加async
並等待SendAsync
,則只會不必要地增加狀態機的開銷。
另請參閱http://blog.stephencleary.com/2016/12/eliding-async-await.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.