我有一个项目列表,对于每个项目,我需要执行几个异步API请求并处理响应,我已经看到了几个实现,并且在执行时间上它们都是相似的,但是我想知道它们之间的区别。
方法1
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);
笔记:
建议使用其中哪一项?如果没有,则建议什么?而且,它们有何不同?为什么执行时间有所不同?
由于您的方法是异步的,因此只需全部调用即可,而不是单独等待它们,而是等待所有方法以确保所有三个异步过程均已完成。然后,您可以await
再次使用来解开结果:
// 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
用于将工作加载到多个线程,但这不是您要在此处执行的操作。您拥有异步进程,这些进程可以同时运行而无需其他线程。
因此,如果您有很多要异步调用的东西,只需将它们全部调用并收集Task
s以便以后等待。
但是,如果请求必须是顺序的,例如分页响应,其中第一个请求将返回下一个页面请求的URL,依此类推?
然后,您将需要使调用彼此依赖,await
以确保在完成异步过程之前可以继续进行调用。例如,如果propertyTwo
呼叫取决于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()
在这种情况下是否合适?
不能。从同步方法中调用异步方法通常不是您应该做的事情。如果调用异步方法,则应使调用方法也异步。有一种方法可以使这项工作正常进行,但是您将不得不处理潜在的僵局,并且通常应该知道您在做什么。另请参阅此问题。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句