【发布时间】:2015-01-07 19:36:12
【问题描述】:
我正在尝试了解 WaitAll 和 WhenAll 的工作原理并遇到以下问题。从方法中获取结果有两种可能的方式:
return Task.WhenAll(tasks).Result.SelectMany(r=> r);return tasks.Select(t => t.Result).SelectMany(r => r).ToArray();
如果我理解正确,第二种情况就像在 tasks 上调用 WaitAll 并在此之后获取结果。
看起来第二种情况的性能要好得多。我知道WhenAll 的正确用法是使用await 关键字,但我仍然想知道为什么这些行的性能差异如此之大。
在分析了系统的流程后,我想我已经弄清楚了如何在一个简单的测试应用程序中对问题进行建模(测试代码基于 I3arnon 答案):
public static void Test()
{
var tasks = Enumerable.Range(1, 1000).Select(n => Task.Run(() => Compute(n)));
var baseTasks = new Task[100];
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
baseTasks[i] = Task.Run(() =>
{
tasks.Select(t => t.Result).SelectMany(r => r).ToList();
});
}
Task.WaitAll(baseTasks);
Console.WriteLine("Select - {0}", stopwatch.Elapsed);
baseTasks = new Task[100];
stopwatch.Restart();
for (int i = 0; i < 100; i++)
{
baseTasks[i] = Task.Run(() =>
{
Task.WhenAll(tasks).Result.SelectMany(result => result).ToList();
});
}
Task.WaitAll(baseTasks);
Console.WriteLine("Task.WhenAll - {0}", stopwatch.Elapsed);
}
看起来问题在于从其他任务(或Parallel 循环)启动任务。在这种情况下,WhenAll 会导致程序的性能更差。这是为什么呢?
【问题讨论】:
-
第二个已经延迟执行,并且只会在您迭代生成的
IEnumerable时进行评估。你确定你的任务真的开始了吗? -
@Chips_100 原始样本使用
ToArray,因此不会延迟执行 -
@Richard - 我必须承认一开始我忘了添加 ToArray。我已经编辑了这个问题,所以现在已经到位了。
-
我不建议像在 Gist 中那样将
Parallel.ForEach与Task结合使用。如果可以最大限度地提高性能,请使用Parallel.ForEach,但如果这是不可能的并且您必须启动Task,那么在高度优化和分区的并行循环中这样做是没有意义的。 -
@MartinLiversage 我按照你的建议做了。谢谢。
标签: c# .net task-parallel-library async-await