具有异步lambda和Task.WaitAll的Task.Factory.StartNew

我正在尝试在任务列表中使用Task.WaitAll 。 事情是任务是一个异步lambda,它打破Tasks.WaitAll因为它永远不会等待。

这是一个示例代码块:

 List tasks = new List(); tasks.Add(Task.Factory.StartNew(async () => { using (dbContext = new DatabaseContext()) { var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); //do long cpu process here... } } Task.WaitAll(tasks); //do more stuff here 

这不会因为异步lambda而等待。 那我该如何等待我的lambda中的I / O操作呢?

Task.Factory.StartNew无法识别async委托,因为没有重载接受返回Task的函数。

这加上其他原因(参见StartNew是危险的 )是你应该在这里使用Task.Run

 tasks.Add(Task.Run(async () => ... 

这不会因为异步lambda而等待。 那我该如何等待我的lambda中的I / O操作呢?

Task.WaitAll不等待异步lambda提供的IO工作完成的原因是因为Task.Factory.StartNew实际上返回了Task 。 由于列表是List (并且Task派生自Task ),因此等待StartNew启动的外部任务,同时忽略 async lambda创建的内部任务。 这就是为什么他们说Task.Factory.StartNew对于异步是危险的

你怎么能解决这个问题? 您可以显式调用Task.Unwrap()以获取内部任务:

 List tasks = new List(); tasks.Add(Task.Factory.StartNew(async () => { using (dbContext = new DatabaseContext()) { var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); //do long cpu process here... } }).Unwrap()); 

或者像其他人说的那样,你可以调用Task.Run

 tasks.Add(Task.Run(async () => /* lambda */); 

此外,既然你想做正确的事情,你会想要使用Task.WhenAll ,为什么是异步等待,而不是同步阻止的Task.WaitAll

 await Task.WhenAll(tasks); 

你可以这样做。

  void Something() { List tasks = new List(); tasks.Add(ReadAsync()); Task.WaitAll(tasks.ToArray()); } async Task ReadAsync() { using (dbContext = new DatabaseContext()) { var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); //do long cpu process here... } } 

你必须使用Task.ContinueWith方法。 像这样

 List tasks = new List(); tasks.Add(Task.Factory.StartNew(() => { using (dbContext = new DatabaseContext()) { return dbContext.Where(r => r.Id = 100).ToListAsync().ContinueWith(t => { var records = t.Result; // do long cpu process here... }); } } }