2017-09-28 74 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