2017-09-28 99 views
7

只是一個簡單的問題。我們在這裏有一些誤解。等待Task.WhenAll vs ..select(async .. =>等待)

我們:

var tasks = files.Select(async fileName => await IngestFileAsync(container, fileName)); 
var results = await Task.WhenAll(tasks); 

我說的第一行還行併發的,但我的同事的同事說,否則。此外,他說第二個await沒有意義,因爲所有 行動已經執行。

是這個代碼,然後是相同的:

var tasks = files.Select(fileName => IngestFileAsync(container, fileName)); 
var results = await Task.WhenAll(tasks); 

爲:

var tasks = files.Select(async fileName => await IngestFileAsync(container, fileName)); 
var results = Task.WhenAll(tasks); 

可能有人照這一些額外的光?

歡呼聲。

新增: oke,所以它會同時運行。

然而,有人可以添加一些額外的信息不同的是,這些代碼段之間是什麼: https://dotnetfiddle.net/lzv2B7 https://dotnetfiddle.net/dMusus

(通知行16,asyncawait)。那兩個有什麼區別? 我會想到的是,異步和等待它會直接啓動,如果沒有,那當談到Await Task.WhenAll(tasks);

添加clearity它會開始 - 這是我的代碼 - :

private async Task<Result> IngestFilesAsync(ICloudBlobContainer container, IEnumerable<string> files) 
    { 
     _logger.LogDebug("Start IngestFilesAsync"); 

     var tasks = files.Select(fileName => IngestFileAsync(container, fileName)); 
     var results = await Task.WhenAll(tasks); 

     _logger.LogDebug("All tasks completed"); 

     if (results.Any(t => t.IsFailure)) 
     { 
      return Result.Fail(string.Join(",", results.Select(f => f.Error))); 
     } 

     return Result.Ok(); 
    } 

    private async Task<Result> IngestFileAsync(ICloudBlobContainer container, string fileName) 
    { 
     _logger.LogDebug("Start IngestFileAsync"); 
     var blob = container.GetBlockBlobReference(fileName); 

     _logger.LogDebug("Blob retrieved"); 

     if (await blob.ExistsAsync()) 
     { 
      using (var memoryStream = new MemoryStream()) 
      { 
       _logger.LogDebug("Start download to stream"); 
       await blob.DownloadToStreamAsync(memoryStream); 
       _logger.LogDebug("To mem downloaded"); 

       _logger.LogDebug("Start ftp-upload"); 

       return await _targetFTP.UploadAsync(memoryStream, fileName); 
      } 
     } 

     _logger.LogWarning("Blob does not exists"); 

     return Result.Fail($"Blob '{fileName}' does not exist"); 
    } 

其中_targetFTP.UploadAsync(memoryStream, fileName);又是一項任務等等。

+1

只需重寫這兩種情況下爲'foreach'環路(而不是'Select') - 你得到答案馬上 – Fabio

+0

這很容易測試:讓你的異步函數'Task.Delay'並測量需要多長時間。 – hvd

+0

@fidor在我的日誌中,我看到了彼此之後的重複行,這表明它們是併發執行的。現在我正在打字,可能會有來自這裏的細線。 –

回答

9

async x => await f()創建一個匿名函數返回一個任務。該任務有效地包裝了由f創建的那個:它將在之後直接完成。特別是,這個匿名函數會盡快返回正在進行的任務。

.Select根據枚舉類型是否是任務之一而行爲不同。它允許在第一個返回的任務仍在進行時直接獲取下一個結果。

代碼片段不是100%相同的,但是您詢問的區別不存在。

差異很小;最明顯的變化可能是在異常處理中。假設你有兩個尚未實現的功能:

Task Sync() => throw new NotImplementedException(); 
async Task Async() => throw new NotImplementedException(); 

這裏,var task = Sync();顯然不會立即失敗。但var task = Async();是不同的:那成功了。這裏的async關鍵字強制創建一個任務,捕獲拋出的異常。

這種相同的區別也適用於.Select(x => Sync()) vs .Select(async x => await Sync())

+0

值得一提的是這種情況:https://dotnetfiddle.net/rI8U3z – gdoron

+0

oke,所以它仍然是併發的。第二個'await' =>'var results = await Task.WhenAll(tasks);'仍然有意義,或者我可以使用Task.WhenAll(tasks); ? –

+0

@RoelantM'Task.WhenAll(任務)'不會阻塞。它返回另一個任務,完成所有原始任務後完成。只是'Task.WhenAll(任務);'因此沒有效果。但是,如果你不需要結果,它可以'等待Task.WhenAll(任務);',沒有'var results'。 – hvd