循环和Task.WhenAll有什么区别?

问题描述 投票:0回答:3

我有一个方法,里面有一个

foreach
循环,在循环内我调用了几个异步操作。我让 ChatGPT 做了审查,他说你可以通过将任务添加到列表中并调用
Task.WhenAll()
来提高性能。

foreach (var name in names)
{
    var some = await SomeMethodAsync1(name);
    await SomeMethodAsync2(name);
    //...
}

重做:

var tasks = names.Select(async n =>
{
    var some = await SomeMethodAsync1(n);
    await SomeMethodAsync2(n);
    //...
}).ToList();
await tasks.WhenAll();

我不明白为什么这会更有成效?毕竟我也是在

Select
里面用await调用异步方法,那和简单的循环有什么区别呢?

c# asynchronous async-await task-parallel-library
3个回答
2
投票

foreach
循环在每次迭代时等待。因此,每次迭代在前一次迭代完成后开始,并且对
SomeMethodAsync1
SomeMethodAsync2
的所有调用都将针对每次迭代单独(连续)完成。

当您执行

Select
时,它会启动代表您定义的
async
lambda 的任务,并且现在并行处理数组中的每个项目。

现在拥有代表并行工作的所有任务,您可以使用

Task.WhenAll()
方法来等待所有任务(顺便说一句。它不能如您所示调用,据我所知,
WhenAll
Task
类上的静态方法) .

这是一个简单的例子:

Console.WriteLine("NoParallel");
await NoParallel();

Console.WriteLine("Parallel");
await Parallel();

async Task NoParallel()
{
    var items = Enumerable.Range(0, 10);
    foreach (var item in items)
    {
        await AsyncMethod();
    }
}

async Task Parallel()
{
    var items = Enumerable.Range(0, 10);
    await Task.WhenAll(items.Select(x => AsyncMethod()));
}

async Task AsyncMethod()
{
    Console.WriteLine("Start async method");
    await Task.Delay(50);
    Console.WriteLine("End async method");
}

哪个会打印(指示什么是并行执行的,什么不是):

NoParallel
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Parallel
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method

1
投票

第一个示例对每个名称依次运行处理代码,而第二个示例则启动所有代码,而不等待其他名称完成。

这是否是一种改进取决于您处理每个名称的内容和方式。


0
投票

根据名称,这应该是非常明显的。 Task.WhenAll 创建一个任务,当列表中的所有任务完成时,该任务都会完成。它将完成操作绑定到任务,因此当每个任务完成时,它会减少剩余的任务计数。一旦达到零,返回的任务将完成。正如名字所说。循环异步函数并等待它们是以串行方式执行它们,而不是同时执行它们。

Tasks.WhenAll 相当于

for(int i = 0; i < alreadyRunningTasks.Count; ++i)
    await alreadyRunningTasks[i];

但需要进行大量检查,并使用基于通知的机制而不是多次等待。 来源

© www.soinside.com 2019 - 2024. All rights reserved.