2016-01-29 43 views
1

經過幾個小時的掙扎之後,我在我的應用程序中發現了一個錯誤。我認爲下面的兩個函數具有相同的行爲,但事實證明它們沒有。Task.WhenAll()和foreach(任務中的var任務)之間的差異

任何人都可以告訴我什麼是真正發生在引擎蓋下,爲什麼他們的行爲以不同的方式?

public async Task MyFunction1(IEnumerable<Task> tasks){ 
    await Task.WhenAll(tasks); 

    Console.WriteLine("all done"); // happens AFTER all tasks are finished 
} 

public async Task MyFunction2(IEnumerable<Task> tasks){ 
    foreach(var task in tasks){ 
     await task; 
    } 

    Console.WriteLine("all done"); // happens BEFORE all tasks are finished 
} 
+4

「發生之前的所有任務完成」看上去很奇怪 - 你可以有[MCVE],顯示這種情況?我沒有看到爲什麼「foreach」會按照您在等待所有任務時描述的方式行事(可能不如「WhenAll」最佳,但仍然全部)。 –

+0

該代碼甚至沒有編譯。 'return'在哪裏? –

+0

'WhenAll'和在一個循環中手動等待它們的一般區別在於,後者將不斷地來回切換到異步方法,需要大量的上下文切換,而前者在內部等待它們而沒有那開銷。 – poke

回答

6

他們將同樣發揮作用如果所有任務成功完成

如果使用WhenAll任何項目失敗,它仍然無法完成,直到所有項目完成後,它會代表AggregatException一個包裝所有錯誤的所有任務。

如果await每一個那麼它很快就會爲它擊中任何項目失敗完成,它會表現的異常爲一個錯誤,而不是任何其他人。


兩個也不同之處在於WhenAll一定會實現整個IEnumerable在一開始,添加任何延續到其他項目之前。如果IEnumerable表示已存在和已啓動的任務的集合,則這不是相關的,但是如果迭代可枚舉的行爲創建和/或啓動任務,則在開始時實現序列將全部並行運行,以及在提取下一個任務之前等待每個任務將按順序執行它們。下面是一個IEnumerable你可以傳遞,由於我這裏描述的將表現:

public static IEnumerable<Task> TaskGeneratorSequence() 
{ 
    for(int i = 0; i < 10; i++) 
     yield return Task.Delay(TimeSpan.FromSeconds(2); 
} 
+0

感謝您的回答。假如沒有例外情況發生,那麼可能導致我描述的情況是什麼? – gisek

+0

@gisek取消或迭代時創建任務的輸入序列。 – Servy

相關問題