如何使用LINQasynchronous地等待任务列表?

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

public async Task<IList<Foo>> GetFoosAndDoSomethingAsync() { var foos = await GetFoosAsync(); var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList(); ... } 

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

这工作在上面...块:

 var list = new List<Foo>(); foreach (var task in tasks) list.Add(await task); return list; 

它做我想要的,但这似乎相当笨拙。 我宁愿写这样简单的东西:

 return tasks.Select(async task => await task).ToList(); 

…但这不能编译。 我错过了什么? 还是这样expression事物是不可能的?

LINQ与async代码无法正常工作,但是你可以这样做:

 var tasks = foos.Select(DoSomethingAsync).ToList(); await Task.WhenAll(tasks); 

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

 var results = await Task.WhenAll(tasks); 

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

 return await Task.WhenAll(tasks); 

为了扩大Stephen的回答,我创build了下面的扩展方法来保持LINQ的stream畅风格 。 你可以这样做

 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); } } } 

使用Task.WaitAllTask.WhenAll任何适用的。

任务。什么时候都应该这样做。

Task.WhenAll的一个问题是它会产生一个并行性。 在大多数情况下,它可能会更好,但有时候你想避免它。 例如,从数据库批量读取数据并将数据发送到某个远程Web服务。 您不希望将所有批次加载到内存中,而是在上一批处理完成之后再次加载数据库。 所以,你必须打破asynchronous性。 这里是一个例子:

 var events = Enumerable.Range(0, totalCount/ batchSize) .Select(x => x*batchSize) .Select(x => dbRepository.GetEventsBatch(x, batchSize).GetAwaiter().GetResult()) .SelectMany(x => x); foreach (var carEvent in events) { } 

注意.GetAwaiter()。GetResult()将其转换为同步。 数据库只会在事件的batchSize被处理一次后才会被打到。