274

下面的代码片段有什么区别?两者都不会使用线程池线程吗?

例如,如果我想为集合中的每个项目调用一个函数,

Parallel.ForEach<Item>(items, item => DoSomething(item));

vs

foreach(var item in items)
{
  Task.Factory.StartNew(() => DoSomething(item));
}
4

4 回答 4

309

第一个是更好的选择。

Parallel.ForEach 在内部使用 aPartitioner<T>将您的集合分发到工作项中。它不会对每个项目执行一项任务,而是将其批处理以降低所涉及的开销。

Task第二个选项将为您的收藏中的每个项目安排一个。虽然结果将(几乎)相同,但这将引入比必要更多的开销,尤其是对于大型集合,并导致整体运行时变慢。

仅供参考 - 如果需要,可以通过对 Parallel.ForEach 使用适当的重载来控制使用的分区器。有关详细信息,请参阅MSDN 上的自定义分区器。

在运行时,主要区别在于第二个将异步执行。这可以通过执行以下操作使用 Parallel.ForEach 复制:

Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));

通过这样做,您仍然可以利用分区程序,但在操作完成之前不要阻塞。

于 2011-02-15T20:37:25.587 回答
93

我做了一个小实验,用“Parallel.For”和“Task”对象运行一个方法“1,000,000,000(十亿)次。

我测量了处理器时间,发现 Parallel 效率更高。Parallel.For 将您的任务划分为小工作项,并以最佳方式在所有内核上并行执行它们。在创建大量任务对象时(仅供参考,TPL 将在内部使用线程池)将移动每个任务上的每次执行,从而在框中产生更大的压力,这从下面的实验中可以看出。

我还制作了一个小视频,它解释了基本的 TPL 并演示了 Parallel.For 如何更有效地利用您的核心http://www.youtube.com/watch?v=No7QqSc5cl8与普通任务和线程相比。

实验一

Parallel.For(0, 1000000000, x => Method1());

实验二

for (int i = 0; i < 1000000000; i++)
{
    Task o = new Task(Method1);
    o.Start();
}

处理器时间比较

于 2013-06-18T18:43:28.250 回答
17

Parallel.ForEach 将优化(甚至可能不启动新线程)并阻塞,直到循环完成,Task.Factory 将为每个项目显式创建一个新任务实例,并在它们完成之前返回(异步任务)。Parallel.Foreach 效率更高。

于 2011-02-15T20:39:19.423 回答
11

在我看来,最现实的情况是任务需要完成繁重的操作。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 毫秒内完成。我重复了很多次。每次第二种方法都快一点。

于 2017-11-28T13:23:40.360 回答