2014-04-06 62 views
0

我在書中讀到了關於下面的區別。等待Task(ReadFromIO)並等待Task.WhenAll(task1,task2)之間的區別;

private async Task GetDataAsync() { 
var task1 = ReadDataFromIOAsync(); 
var task2 = ReadDataFromIOAsync(); 
// Here we can do more processing 
// that doesn't need the data from the previous calls. 
// Now we need the data so we have to wait 
await Task.WhenAll(task1, task2); 
// Now we have data to show. 
lblResult.Content = task1.Result; 
lblResult2.Content = task2.Result; 
} 

private async Task GetDataAsync() { 
var task1 = ReadDataFromIOAsync(); 
var task2 = ReadDataFromIOAsync(); 
lblResult.Content = await task1; 
lblResult2.Content = await task2; 
} 

我明白在第一個方法的await語句中發生了什麼。但對於第二個,儘管我理解了邏輯,但我無法理解第二個實現與第一個實現相比的缺陷。在書中,他們提到編譯器重寫這個方法兩次。我所理解的是因爲兩個等待電話,可能比第一個電話時間延遲更多,因爲我們在此分別呼叫等待每個任務。有人能以更好的方式解釋我嗎?

回答

1

我不知道你的書想要做什麼,但我同意你對它的互操作性的初步猜測。

潛在的問題是,可能有一段時間,其中lblResult顯示新的數據和lblResult2顯示舊的數據,如果task2時間超過task1更長的時間來處理。在第一種方法中,您要等到兩個任務完成後才能同時更新兩個標籤(並且在退出方法時同時在屏幕上重新繪製)。在第二種方法中,您更新第一個標籤,然後您給消息循環重新繪製屏幕的機會,然後稍後您更新第二個標籤並讓該值在屏幕上更新。

我想你會有一個稍微更復雜的狀態機的第二個例子,但這樣的開銷可以忽略不計,我相信這本書試圖指出你和我都提出的問題。

1

本質上講第一種方法做的是這樣的:

  1. 開始task1
  2. 開始task2
  3. 異步等待task1task2完成

第二個實現不這個:

  1. 開始task1
  2. 開始task2
  3. 異步等待task1完成。
  4. 一旦task1完成,恢復執行,然後異步等待task2完成。

因此,第二種方法是單獨等待每項任務的結果,而不是等待兩項任務完成。在在task2之前完成的情況下,代碼將恢復執行,然後立即返回,這將導致額外的上下文切換,這可能需要額外的時間。同樣對於多個等待的情況,編譯器可能最終生成更復雜的狀態機,但是其效果可以忽略不計。

在這兩種情況下,您都沒有使用結果,直到兩項任務都完成,因此應用程序的行爲不應該太差。