以下代码段之间有什么区别?两个都不会使用线程池线程吗?
例如,如果我想为集合中的每个项目调用一个函数,
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
答案 0 :(得分:286)
第一个是更好的选择。
Parallel.ForEach在内部使用Partitioner<T>
将您的集合分发到工作项中。它不会对每个项目执行一项任务,而是批量处理以降低所涉及的开销。
第二个选项会在您的收藏中为每个项目安排一个Task
。虽然结果将(几乎)相同,但这将引入远远超过必要的开销,特别是对于大型集合,并导致整体运行时间变慢。
仅供参考 - 如果需要,可以使用适当的overloads to Parallel.ForEach来控制使用的分区程序。有关详细信息,请参阅MSDN上的Custom Partitioners。
在运行时,主要的区别是第二个将异步。这可以使用Parallel.ForEach复制:
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
通过这样做,您仍然可以利用分区程序,但在操作完成之前不要阻塞。
答案 1 :(得分:80)
我做了一个小实验,用“Parallel.For”运行方法“1000000000”次,用“Task”对象运行一次。
我测量了处理器时间,发现Parallel效率更高。 Parallel.For将您的任务划分为小工作项,并以最佳方式并行地在所有核上执行它们。虽然创建大量任务对象(FYI TPL将在内部使用线程池)将在每个任务上执行每次执行,从而在框中产生更多压力,这从下面的实验中可以看出。
我还创建了一个解释基本TPL的小视频,并演示了与正常任务和线程相比,Parallel.For如何更有效地利用核心http://www.youtube.com/watch?v=No7QqSc5cl8。
实验1
Parallel.For(0, 1000000000, x => Method1());
实验2
for (int i = 0; i < 1000000000; i++)
{
Task o = new Task(Method1);
o.Start();
}
答案 2 :(得分:17)
Parallel.ForEach将优化(甚至可能不启动新线程)并阻塞直到循环结束,Task.Factory将为每个项显式创建一个新的任务实例,并在它们完成之前返回(异步任务)。 Parallel.Foreach效率更高。
答案 3 :(得分:8)
在我看来,最现实的情况是任务需要繁重的操作才能完成。 Shivprasad的方法更多地关注对象创建/内存分配而不是计算本身。我做了一个研究,调用以下方法:
public static double SumRootN(int root)
{
double result = 0;
for (int i = 1; i < 10000000; i++)
{
result += Math.Exp(Math.Log(i) / root);
}
return result;
}
执行此方法大约需要0.5秒。
我使用Parallel调用它200次:
Parallel.For(0, 200, (int i) =>
{
SumRootN(10);
});
然后我用老式的方式打了200次电话:
List<Task> tasks = new List<Task>() ;
for (int i = 0; i < loopCounter; i++)
{
Task t = new Task(() => SumRootN(10));
t.Start();
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
第一个案例在26656ms完成,第二个案例在24478ms完成。我重复了很多次。每次第二种方法都快得多。