C# 发送多个异步API请求并并行处理响应的正确方法是什么?
我有一个项目列表,对于每个项目,我需要执行几个异步API请求并处理响应,我看到了几个实现,它们在执行时间方面都很相似,但我想知道它们之间的区别 方法1C# 发送多个异步API请求并并行处理响应的正确方法是什么?,c#,asp.net-core,task,parallel.foreach,C#,Asp.net Core,Task,Parallel.foreach,我有一个项目列表,对于每个项目,我需要执行几个异步API请求并处理响应,我看到了几个实现,它们在执行时间方面都很相似,但我想知道它们之间的区别 方法1 Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = 10 }, item => { var response = item.propertyOne.GetAsync().GetAwaiter().GetResul
Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = 10 }, item =>
{
var response = item.propertyOne.GetAsync().GetAwaiter().GetResult();
//process it
var response = item.propertyTwo.GetAsync().GetAwaiter().GetResult();
//process it
var response = item.propertyThree.GetAsync().GetAwaiter().GetResult();
//process it
});
方法2
Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = 10 }, item =>
{
Task.Run(async () =>
{
var response = await item.propertyOne.GetAsync();
}).GetAwaiter().GetResult();
//process it
Task.Run(async () =>
{
var response = await item.propertyTwo.GetAsync();
}).GetAwaiter().GetResult();
//process it
Task.Run(async () =>
{
var response = await item.propertyThreee.GetAsync();
}).GetAwaiter().GetResult();
//process it
});
方法3假设应用了某种边界机制,以避免任务充斥系统
List<Task> tasksToAwait = new List<Task>();
foreach (var item in items)
{
tasksToAwait.Add(Task.Run(async () =>
{
var response = await item.propertyOne.GetAsync();
//process it
var response = await item.propertyTwo.GetAsync();
//process it
var response = await item.propertyThree.GetAsync();
//process it
}));
}
await Task.WhenAll(taskToAwait);
List tasksToAwait=new List();
foreach(项目中的var项目)
{
taskstowait.Add(Task.Run)(异步()=>
{
var response=wait item.propertyOne.GetAsync();
//处理它
var response=wait item.propertyTwo.GetAsync();
//处理它
var response=wait item.propertytree.GetAsync();
//处理它
}));
}
等待任务。WhenAll(任务等待);
注:
- 我使用GetAwaiter().GetResult()而不是Wait(),因为它不接受异常
- 为了处理响应,必须以某种方式等待请求
- 这是作为后台任务执行的,所以我不介意阻塞调用线程
- 第三种方法略快于其他两种方法,第二种方法略快于第一种方法
- 我无法控制通过GetAsync()调用的异步API
推荐哪一种?如果没有,你有什么建议?另外,它们有什么不同?为什么会有执行时间差异?使用Parallel.Foreach并将所有任务添加到列表中,使用WhenAll方法等待所有任务完成。由于您的方法是异步的,只需调用它们,而不是单独等待它们,等待所有人,以确保所有三个异步进程都已完成。然后,您可以再次使用
wait
打开结果:
// start all asynchronous processes at once for all three properties
var oneTask = item.propertyOne.GetAsync();
var twoTask = item.propertyTwo.GetAsync();
var threeTask = item.propertyThree.GetAsync();
// await the completion of all of those tasks
await Task.WhenAll(oneTask, twoTask, threeTask);
// access the individual results; since the tasks are already completed, this will
// unwrap the results from the tasks instantly:
var oneResult = await oneTask;
var twoResult = await twoTask;
var threeResult = await threeTask;
通常,在处理异步内容时,避免调用.GetResult()
、.Result
或.Wait()
。如果您有一个异步进程,这两种方法都不会有任何好处
要答复您的评论: 我想代码应该在并行foreach中,对吗 不,您不会在这里使用
Parallel.ForEach
,因为您仍在调用异步进程Parallel.ForEach
用于将工作加载到多个线程,但这不是您想要在这里执行的操作。您有异步进程,它们都可以并发运行,而不需要额外的线程
因此,如果有很多东西需要异步调用,只需调用所有这些东西并收集任务
等待它们
但是,如果请求必须是连续的,例如分页响应,其中第一个请求将返回下一个页面请求的URL等等,该怎么办
然后,您需要使调用相互依赖,并使用wait
确保异步过程完成后才能继续。例如,如果两个propertyTo
调用依赖于propertyOne
,那么您仍然可以首先进行所有propertyOne
调用,并且只有在这些调用完成后才能继续
如果将逻辑拆分为单独的方法,您会注意到这会变得更容易:
var itemTasks = items.Select(item => GetPropertiesOneTwoThreeAsync(item));
await Task.WhenAll(itemTasks);
private Task GetPropertiesOneTwoThreeAsync(Item item)
{
// get the properties sequentially
var oneResult = await item.propertyOne.GetAsync();
var twoResult = await item.propertyTwo.GetAsync();
var threeResult = await item.propertyThree.GetAsync();
}
另外,如果调用方法不是异步的,那么Task.WhenAll(oneTask,twoTask,threeTask).GetAwaiter().GetResult()在这种情况下是否合适
不。从同步方法调用异步方法通常不是您应该做的事情。如果调用异步方法,则应使调用方法也异步。有一种方法可以让这一切顺利进行,但你必须处理好潜在的僵局,并且通常应该知道你在做什么。另请参见。好问题,我通常只将任务存储在一个列表中,然后执行。好主意,我假设代码将进入并行foreach中,对吗?但是,如果请求必须是连续的,例如分页响应,其中第一个请求将返回下一个页面请求的URL等等,该怎么办?另外,如果调用方法不是异步的,Task.WhenAll(oneTask,twoTask,threeTask).GetAwaiter().GetResult()在这种情况下是否合适?Thanks@DaniMazahreh我编辑了我的答案以回答您的后续问题。感谢您的后续提问,最后一段代码类似于我将坚持使用的第三种方法。除了加快创建任务,这还能完成什么?