Parallel.ForEachAsync 是否可以替代普通的 for 循环 + 附加到任务列表 (async wait Task.Run)+ WhenAll?

var*_*ble 8 c# asynchronous async-await parallel.foreachasync

假设我想发出并行 API post 请求。

在 for 循环中,我可以将 http post 调用附加到任务列表中(使用 Task.Run 调用的每个任务),然后等待所有任务完成使用await Task.WhenAll. 因此,在等待网络请求完成时,控制权将交给调用者。实际上,API 请求将并行发出。

同样,我可以使用Parallel.ForEachAsync它将自动执行WhenAll并将控制权返回给调用者。所以我想问是否ForEachAsync可以替换普通的 for 循环列表(async wait Task.Run)和WhenAll

The*_*ias 14

不,Parallel.ForEachAsync与简单使用 API 相比,该 API 有相当多的差异Task.WhenAll

  1. 房间里的大象:await Task.WhenAll返回一个包含异步操作结果的数组。相反,Parallel.ForEachAsync返回一个裸露的Task。如果您想要结果,则必须依赖副作用,例如更新 aConcurrentQueue<T>作为异步操作的一部分。

  2. 在线程上并行调用提供的异步委托(可Parallel.ForEachAsync配置。相反,使用 的常见模式是在当前线程上按顺序创建s。这引起了人们对在 ASP.NET 应用程序中使用 的担忧,其中卸载工作可能会产生可伸缩性影响ThreadPoolTask.WhenAllTaskParallel.ForEachAsyncThreadPool

  3. 调用Parallel.ForEachAsync异步委托并等待生成的任务,同时强制执行等于的最大并发级别Environment.ProcessorCount。此行为可通过MaxDegreeOfParallelism选项进行配置。相反,使用的常见模式Task.WhenAll是一次创建所有任务,不施加并发限制。

  4. 使用的常见模式Task.WhenAll是假设创建所有任务不可能中途失败,因此不对这种可能性采取任何预防措施。如果这种情况真的发生,“即发即弃”任务可能会被泄露。这对于 API 来说是不可能的Parallel.ForEachAsync

  5. Parallel.ForEachAsync一旦异步委托调用或创建的第一个错误发生,它将停止调用异步委托,Task然后在等待所有已创建的任务后传播包含迄今为止发生的所有错误的失败。它还提供了一种机制,用于在发生错误时取消正在进行的其他任务(CancellationToken作为 lambda 中的第二个参数传递)。相反,它Task.WhenAll总是等待所有任务完成。这意味着您可能需要等待更长时间才能最终收到AggregateException包含所有失败任务的错误的信息。