运行 并行任务
Running Tasks in parallel
我无法理解为什么这似乎无法 运行 并行任务:
var tasks = new Task<MyReturnType>[mbis.Length];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
}
Parallel.ForEach(tasks, task => task.Start());
通过单步执行,我看到这一行一被评估:
tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
任务开始。我想将所有新任务添加到列表中,然后并行执行它们。
如果 GetAllRouterInterfaces
是一个 async
方法,则生成的 Task
将已经启动(请参阅 this answer 了解更多说明)。
这意味着 tasks
将包含多个任务,所有这些任务都是 运行 并行的,而无需随后调用 Parallel.ForEach
。
您可能希望等待 tasks
中的所有条目完成,您可以使用 await Task.WhenAll(tasks);
。
所以你最终应该得到:
var tasks = new Task<MyReturnType>[mbis.Length];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
}
await Task.WhenAll(tasks);
根据评论更新
似乎尽管 GetAllRouterInterfaces
是 async
并且 return 正在 Task
它仍在发出同步 POST 请求(大概在任何其他 await
).这可以解释为什么在发出此请求时每次调用 GetAllRouterInterfaces
都会阻塞,因此并发性最小。理想的解决方案是发出异步 POST 请求,例如:
await webclient.PostAsync(request).ConfigureAwait(false);
这将确保您的 for
循环不被阻塞并且请求是并发的。
对话后进一步更新
您似乎无法使 POST 请求异步并且 GetAllRouterInterfaces
实际上不执行任何异步工作,因此我建议如下:
- 从
GetAllRouterInterfaces
中删除 async
并将 return 类型更改为 MyReturnType
像这样并行调用GetAllRouterInterfaces
var routerInterfaces = mbis.AsParallel()
.Select(mbi => CAS.Service.GetAllRouterInterfaces(mbi, 3));
不知道我理解的对不对。
首先,如果 GetAllRouterInterfaces 是 returns 任务,您必须等待结果。
使用 Parallel.ForEach 你不能像现在这样等待任务,但你可以做类似这样的事情:
public async Task RunInParallel(IEnumerable<TWhatEver> mbisItems)
{
//mbisItems == your parameter that you want to pass to GetAllRouterInterfaces
//degree of cucurrency
var concurrentTasks = 3;
//Parallel.Foreach does internally something like this:
await Task.WhenAll(
from partition in Partitioner.Create(mbisItems).GetPartitions(concurrentTasks)
select Task.Run(async delegate
{
using (partition)
while (partition.MoveNext())
{
var currentMbis = partition.Current;
var yourResult = await GetAllRouterInterfaces(currentMbis,3);
}
}
));
}
我无法理解为什么这似乎无法 运行 并行任务:
var tasks = new Task<MyReturnType>[mbis.Length];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
}
Parallel.ForEach(tasks, task => task.Start());
通过单步执行,我看到这一行一被评估:
tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
任务开始。我想将所有新任务添加到列表中,然后并行执行它们。
如果 GetAllRouterInterfaces
是一个 async
方法,则生成的 Task
将已经启动(请参阅 this answer 了解更多说明)。
这意味着 tasks
将包含多个任务,所有这些任务都是 运行 并行的,而无需随后调用 Parallel.ForEach
。
您可能希望等待 tasks
中的所有条目完成,您可以使用 await Task.WhenAll(tasks);
。
所以你最终应该得到:
var tasks = new Task<MyReturnType>[mbis.Length];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
}
await Task.WhenAll(tasks);
根据评论更新
似乎尽管 GetAllRouterInterfaces
是 async
并且 return 正在 Task
它仍在发出同步 POST 请求(大概在任何其他 await
).这可以解释为什么在发出此请求时每次调用 GetAllRouterInterfaces
都会阻塞,因此并发性最小。理想的解决方案是发出异步 POST 请求,例如:
await webclient.PostAsync(request).ConfigureAwait(false);
这将确保您的 for
循环不被阻塞并且请求是并发的。
对话后进一步更新
您似乎无法使 POST 请求异步并且 GetAllRouterInterfaces
实际上不执行任何异步工作,因此我建议如下:
- 从
GetAllRouterInterfaces
中删除async
并将 return 类型更改为MyReturnType
像这样并行调用
GetAllRouterInterfaces
var routerInterfaces = mbis.AsParallel() .Select(mbi => CAS.Service.GetAllRouterInterfaces(mbi, 3));
不知道我理解的对不对。
首先,如果 GetAllRouterInterfaces 是 returns 任务,您必须等待结果。
使用 Parallel.ForEach 你不能像现在这样等待任务,但你可以做类似这样的事情:
public async Task RunInParallel(IEnumerable<TWhatEver> mbisItems)
{
//mbisItems == your parameter that you want to pass to GetAllRouterInterfaces
//degree of cucurrency
var concurrentTasks = 3;
//Parallel.Foreach does internally something like this:
await Task.WhenAll(
from partition in Partitioner.Create(mbisItems).GetPartitions(concurrentTasks)
select Task.Run(async delegate
{
using (partition)
while (partition.MoveNext())
{
var currentMbis = partition.Current;
var yourResult = await GetAllRouterInterfaces(currentMbis,3);
}
}
));
}