如何使用LINQ异步等待任务列表?

Mat*_*int 81 c# linq async-await

我有一个我创建的任务列表,如下所示:

public async Task<IList<Foo>> GetFoosAndDoSomethingAsync()
{
    var foos = await GetFoosAsync();

    var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();

    ...
}
Run Code Online (Sandbox Code Playgroud)

通过使用.ToList(),任务都应该开始.现在我想等待完成并返回结果.

这适用于上面的...块:

var list = new List<Foo>();
foreach (var task in tasks)
    list.Add(await task);
return list;
Run Code Online (Sandbox Code Playgroud)

它做我想要的,但这似乎相当笨拙.我宁愿写一些更简单的东西:

return tasks.Select(async task => await task).ToList();
Run Code Online (Sandbox Code Playgroud)

...但这不会编译.我错过了什么?或者是不可能以这种方式表达事物?

Ste*_*ary 126

LINQ不能与async代码完美配合,但您可以这样做:

var tasks = foos.Select(DoSomethingAsync).ToList();
await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

如果您的任务都返回相同类型的值,那么您甚至可以这样做:

var results = await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

这很不错.WhenAll返回一个数组,所以我相信你的方法可以直接返回结果:

return await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

  • 只是想指出这也适用于`var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList();` (11认同)
  • @EhsanSajjad:如果该操作基于I / O,则可以使用`async`减少线程;如果它已经受CPU限制并且已经在后台线程上,那么`async`不会提供任何好处。 (3认同)
  • Linq与异步代码无法完美配合的背后原因是什么? (2认同)
  • @EhsanSajjad:因为 LINQ to Objects 同步处理内存中的对象。一些有限的东西可以工作,比如“选择”。但大多数不会,比如“Where”。 (2认同)

Cle*_*ent 9

为了扩展Stephen的答案,我创建了以下扩展方法以保持流畅的LINQ 风格.然后你可以做

await someTasks.WhenAll()

namespace System.Linq
{
    public static class IEnumerableExtensions
    {
        public static Task<T[]> WhenAll<T>(this IEnumerable<Task<T>> source)
        {
            return Task.WhenAll(source);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 就个人而言,我会将您的扩展方法命名为"ToArrayAsync" (9认同)