簡體   English   中英

調用異步方法和Task.Run異步方法之間的區別

[英]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將不會給您帶來任何好處。

編輯:

現在您已經添加了實現,我看到兩件事:

  1. 我不確定您的同步數據分析需要占用多少CPU,但是對於UI而言,它可能足夠響應。
  2. 您無需等待異步操作。 它需要看起來像這樣:

     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限制,並且將阻塞原始調用者的線程,直到循環完成。

因此,無論是否awaitSyncContacts返回的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 awaitSyncContactsasync / 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.

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