asnyc方法中.ForEach(async x => {await})和foreach {await}之间的区别

时间:2017-10-06 12:46:30

标签: c# async-await task

这两个简化代码之间有什么区别,特别是foreach部分。

public async Task UploadFilesAsync(IEnumerable<UploadFile> uploads)
{
    uploads.ToList().ForEach(async upload => 
    {
     await UploadFileAsync(upload);
    });
}

public async Task UploadFilesAsync(IEnumerable<UploadFile> uploads)
{
    foreach (upload in uploads)
     {
      await UploadFileAsync(upload);
     }
}

我知道第一个基本上创建了一个异步void操作,由于各种原因这不是最好的解决方案,但是第二个会做同样的事情,还是更多的公认实践呢?

2 个答案:

答案 0 :(得分:0)

由于第一个方法包含一个不属于异步执行的foreach,它将启动内部匿名方法并退出。在UploadFileAsync方法完成后,匿名方法将完成。

第二个版本按顺序运行,因为foreach等待每次迭代(在调用MoveNext()之前),因此UploadFileAsync方法将在所有内部调用完成后退出。

您可以使用以下代码对其进行测试:

Y is :['Fractures' 'Second degree heat (thermal) burns'
     'Traumatic injuries and disorders, unspecified' ..., 'Fractures'
     'Poisoning, including poisoning-related asphyxia' 'Fractures']

所以,这不是对错的问题。它只是关于你想要实现的目标。如果您在UploadFilesAsync方法中创建了资源并希望在内部匿名方法中使用它们,那么使用第一个版本很容易遇到闭包问题。由于该方法在上载完成之前退出,因此在内部方法可以使用它们之前,将释放您创建的资源。

答案 1 :(得分:-1)

首先要做的事情。您的重点似乎是代码的异步/等待部分,所以我将首先回答。

我不相信从这个角度来看有任何不同

但除此之外还有一些有趣的差异

  1. 通过在方法#1中调用uploads.ToList(),您正在迭代完整的IEnumerable<UploadFile> uploads,而在方法#2中,您正在使用延迟执行的力量
  2. 使用List<T>.ForEach,您需要创建一个lambda expression,其List<T>将为await中的每个元素调用其主体。如果它被调用很多次并且相当轻量级的工作,这可能相当昂贵。您可以在方法#2
  3. 中明确避免这种不必要的开销
  4. 异常处理将是方法#1的痛苦,而方法#2允许使用典型的异常处理机制
  5. 现在,了解当您的代码遇到Task调用时,几乎会暂停UploadFileAsync(upload)完成代码。除非您想在该调用之后处理await'ing的结果,否则您可以在没有public async Task UploadFilesAsync(IEnumerable<UploadFile> uploads) { foreach (upload in uploads) { UploadFileAsync(upload); } } 的情况下继续执行并继续执行,直到需要这些结果为止

    Core Data

    这样,您可以让来电者决定何时等待结果。