Task.WhenAll的顺序版本

本文关键字:版本 顺序 WhenAll Task | 更新日期: 2023-09-27 17:49:31

是否存在与Task.WhenAll相似但不平行的非阻塞Task.WaitAll ?

我写了这个,但也许它是内置的?

public async Task<IEnumerable<T>> AwaitAllAsync<T>(IEnumerable<Task<T>> tasks)
{
    List<T> result = new List<T>();
    foreach(var task in tasks)
    {
        result.Add(await task);
    }
    return result;
}

我想知道是否有一种内置的方式来等待所有任务异步完成,但顺序的方式。

考虑以下代码:

public class SaveFooCommandHandler : ICommandHandler<SaveFooCommand>
{
   private readonly IBusinessContext context;
   public SaveFooCommandHandler(IBusinessContext context)
   {
      this.context = context;
   }
   public async Task Handle(SaveFooCommand command)
   {
      var foos = (await Task.WhenAll(command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id))).ToList()
      ...
   }
}

会失败,但是

var foos = await context.AwaitAllAsync(command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id));

将不,context.FindAsyncdbcontext.Set<T>().FindAsync的抽象

您可以使用await context.Set<Foo>().Where(f => command.Foos.Contains(f.Id)).ToListAsync(),但是这个示例已经简化了。

Task.WhenAll的顺序版本

我认为核心误解是围绕Task类型。在异步代码中,Task总是已经在运行。所以这是没有意义的:

是否存在与Task.WhenAll相似但不parallel concurrent的非阻塞Task.WaitAll ?

如果你有一组任务,它们都已经开始

我想知道是否有一种方法可以等待所有任务以异步但顺序的方式完成。

当然,您可以按顺序await它们。这方面的标准模式是在foreach循环中使用await,就像你发布的方法一样。

然而,顺序- await有效的唯一原因是因为您的LINQ查询是惰性求值的。特别是,如果您重新定义任务集合,它将失败。

var tasks = command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id));
var foos = await context.AwaitAllAsync(tasks);

,这个失败:

var tasks = command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id))
    .ToList();
var foos = await context.AwaitAllAsync(tasks);

内部,Task.WhenAll具体化你的任务序列,所以它知道有多少任务需要等待。

但这真的是离题了。您试图解决的真正问题是如何串行执行异步代码,这是最容易完成的使用foreach:

var foos = new List<Foo>();
foreach (var fooId in command.Foos.Select(f => f.Id))
  foos.Add(await context.FindAsync<Foo>(fooId));