[英]Difference between calling .Wait() on an async method, and Task.Run().Wait()
[英]Difference between calling an async method and Task.Run an async method
我的視圖模型中有一個方法
private async void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
this.SyncContacts();
}
}
private async Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// do synchronous data analysis
}
// ...
// AddContacts is an async method
CloudInstance.AddContacts(contactsToUpload);
}
當我從UI命令調用SyncData
並同步大量數據時,UI凍結。 但是當我用這種方法調用SyncContacts
時
private void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
Task.Run(() => this.SyncContacts());
}
}
一切順利。 他們不應該一樣嗎? 我在想,不使用等待調用異步方法會創建一個新線程。
他們不應該一樣嗎? 我在想,不使用等待調用異步方法會創建一個新線程。
不, async
不會神奇地為其方法調用分配新線程。 async-await
主要是關於利用自然的異步API,例如對數據庫的網絡調用或遠程Web服務。
使用Task.Run
,將顯式使用線程池線程執行委托。 如果您使用async
關鍵字標記方法,但不在內部await
任何內容,它將同步執行。
我不確定您的SyncContacts()
方法實際上是做什么的(因為您沒有提供它的實現),但是將它本身標記為async
將不會給您帶來任何好處。
編輯:
現在您已經添加了實現,我看到兩件事:
您無需等待異步操作。 它需要看起來像這樣:
private async Task SyncDataAsync(SyncMessage syncMessage) { if (syncMessage.State == SyncState.SyncContacts) { await this.SyncContactsAsync(); } } private Task SyncContactsAsync() { foreach(var contact in this.AllContacts) { // do synchronous data analysis } // ... // AddContacts is an async method return CloudInstance.AddContactsAsync(contactsToUpload); }
你的行Task.Run(() => this.SyncContacts());
真正的作用是創建一個新任務,將其啟動並將其返回給調用方(在您的情況下,該目標不再用於任何其他目的)。 這就是為什么它將在后台工作並且UI將繼續工作的原因。 如果需要(a)等待任務完成,則可以使用await Task.Run(() => this.SyncContacts());
。 如果只想確保在返回SyncData方法時SyncContacts已完成,則可以使用返回任務,並在SyncData方法結束時等待它。 正如評論中所建議的:如果您對任務是否完成不感興趣,則可以將其退回。
但是,Microsoft建議不要混合使用阻塞代碼和異步代碼,並且異步方法以Async( https://msdn.microsoft.com/zh-cn/magazine/jj991977.aspx )結尾。 因此,當您不使用await關鍵字時,應該考慮重命名方法,並且不要使用異步標記方法。
只是為了闡明UI凍結的原因-在緊密的foreach
循環中完成的工作可能受CPU限制,並且將阻塞原始調用者的線程,直到循環完成。
因此,無論是否await
從SyncContacts
返回的Task
,調用AddContactsAsync
之前的CPU綁定工作仍將在調用者的線程上同步發生並阻塞。
private Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// ** CPU intensive work here.
}
// Will return immediately with a Task which will complete asynchronously
return CloudInstance.AddContactsAsync(contactsToUpload);
}
(Re:不,為什么async / return await
在SyncContacts
上async / return await
SyncContacts
請參見Yuval的要點-使方法異步並等待結果在這種情況下會很浪費 )
對於WPF項目,應該可以使用Task.Run
在調用線程Task.Run
完成CPU綁定工作(但對於MVC或WebAPI Asp.Net項目則不是這樣 )。
另外,假設contactsToUpload
映射工作是線程安全的,並且您的應用程序充分利用了用戶的資源,那么您還可以考慮並行化映射以減少總體執行時間:
var contactsToUpload = this.AllContacts
.AsParallel()
.Select(contact => MapToUploadContact(contact));
// or simpler, .Select(MapToUploadContact);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.