下面的代码片段之间有什么区别?两者不都使用线程池线程吗?
例如,如果我想为集合中的每一项调用一个函数,
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
下面的代码片段之间有什么区别?两者不都使用线程池线程吗?
例如,如果我想为集合中的每一项调用一个函数,
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
当前回答
第一种是更好的选择。
平行的。ForEach在内部使用Partitioner<T>将您的集合分发到工作项中。它不会对每个项目执行一个任务,而是批量执行以降低所涉及的开销。
第二个选项将为您收集的每个项目安排一个任务。虽然结果(几乎)是相同的,但这会引入比必要的多得多的开销,特别是对于大型集合,并导致整体运行时变慢。
供参考-分区器使用可以通过使用适当的重载并行控制。每个人,如果需要的话。具体请参见MSDN上的自定义分区器。
在运行时,主要的区别在于第二种方式是异步的。这可以使用Parallel进行复制。通过做:
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
通过这样做,您仍然可以利用分区器,但是在操作完成之前不要阻塞。
其他回答
在我看来,最现实的情况是当任务需要完成繁重的操作时。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());
第一个案例在26656毫秒内完成,第二个案例在24478毫秒内完成。我重复了很多次。每次第二种方法都要快一点。
我做了一个小实验,用“Parallel”运行一个方法“1,000,000,000(10亿)次”。和一个带有“Task”对象的。
我测量了处理器时间,发现并行更有效。平行的。For将任务划分为小的工作项,并以最佳方式在所有核心上并行执行。在创建大量任务对象(供参考,TPL将在内部使用线程池)时,将移动每个任务的每次执行,从而在方框中产生更多压力,这从下面的实验中可以明显看出。
我还创建了一个小视频,解释了基本的TPL,并演示了如何并行。用于更有效地利用您的核心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();
}
第一种是更好的选择。
平行的。ForEach在内部使用Partitioner<T>将您的集合分发到工作项中。它不会对每个项目执行一个任务,而是批量执行以降低所涉及的开销。
第二个选项将为您收集的每个项目安排一个任务。虽然结果(几乎)是相同的,但这会引入比必要的多得多的开销,特别是对于大型集合,并导致整体运行时变慢。
供参考-分区器使用可以通过使用适当的重载并行控制。每个人,如果需要的话。具体请参见MSDN上的自定义分区器。
在运行时,主要的区别在于第二种方式是异步的。这可以使用Parallel进行复制。通过做:
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
通过这样做,您仍然可以利用分区器,但是在操作完成之前不要阻塞。
平行的。ForEach将优化(甚至可能不会启动新的线程)并阻塞,直到循环完成,而Task。Factory将为每个项目显式地创建一个新的任务实例,并在它们完成之前返回(异步任务)。 平行的。Foreach更有效率。