foreach vs Task.WhenAll() - 执行有什么不同吗?

时间:2017-10-20 08:04:32

标签: c# asynchronous async-await

在我的应用程序的管道上,有一个选项可以注入一组预请求处理程序,这些处理程序可以在首次进入管道时改变请求。管道是完全异步的,因此必须等待这些预请求处理程序调用(以及其他所有内容)。我有几种不同的方法可以调用这些处理程序,我想知道它们之间是否存在任何差异,如果是这样,差异会是什么?例如,是否会以相同的顺序调用各种处理程序?哪个选项可能提供最佳性能?

选项1:foreach

this.preRequestHandlers.ForEach(async handler => await handler.Handle(request));

选项2:ForEach()

await Task.WhenAll(this.preRequestHandlers.Select(handler => handler.Handle(request)));

选项3:Task.WhenAll()

{{1}}

2 个答案:

答案 0 :(得分:3)

假设您通过同时运行各个处理程序获得(您将知道这是否为真),选项3是最好的,因为它将在接近零时创建一个任务集合,然后可能并行执行,然后等待一次完成所有任务。选项1将依次串行执行每个处理程序。因此,如果每个处理程序需要4秒钟,并且同时运行多个仍需要约4秒,则选项3将在~4秒内完成。选项1将以4 x(预请求处理程序数)秒完成。

如果你需要处理程序按顺序执行,一次只执行一个处理程序,那么选项3就不合适了。

选项2并不适合任何事情,因为它将在处理程序任务完成之前完成。出于这个原因,可以看到任何现有的SO答案; e.g:

How can I use Async with ForEach?

C# async await using LINQ ForEach()

Any point to List<T>.ForEach() with Async?

答案 1 :(得分:1)

前两个选项将启动并等待当前线程中的所有任务,而第三个选项生成一个任务(等待内部任务)等待当前线程。

第二个调用了一个附加函数,它调用另一个函数(lambda)foreach元素,因此它是选项1的额外开销。

在第3个版本中,原始集合被投射到另一个集合(使用Select),与前两个选项相比,该集合的速度变慢。

所以 我认为最好的方法是选项1 ,它可以减少任务创建和功能堆叠开销。

作为第一选项的性能改进,我建议您使用 for 循环而不是 foreach good explanation here