Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Parallel.ForEach vs.Task.Factory.StartNew_C#_C# 4.0_Task Parallel Library_Parallel Extensions - Fatal编程技术网

C# Parallel.ForEach vs.Task.Factory.StartNew

C# Parallel.ForEach vs.Task.Factory.StartNew,c#,c#-4.0,task-parallel-library,parallel-extensions,C#,C# 4.0,Task Parallel Library,Parallel Extensions,以下代码片段之间有什么区别?两个线程不是都使用线程池线程吗 例如,如果我想为集合中的每个项调用函数 Parallel.ForEach<Item>(items, item => DoSomething(item)); vs foreach(var item in items) { Task.Factory.StartNew(() => DoSomething(item)); } Parallel.ForEach(items,item=>DoSomething(it

以下代码片段之间有什么区别?两个线程不是都使用线程池线程吗

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

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

vs

foreach(var item in items)
{
  Task.Factory.StartNew(() => DoSomething(item));
}
Parallel.ForEach(items,item=>DoSomething(item));
vs
foreach(项目中的var项目)
{
Task.Factory.StartNew(()=>DoSomething(项目));
}

第一个是更好的选择

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

第二个选项将为集合中的每个项目安排一个
任务。虽然结果(几乎)相同,但这将引入远远超过必要的开销,特别是对于大型集合,并导致总体运行时变慢

仅供参考-如果需要,可通过使用适当的控制器控制所使用的分区器。有关详细信息,请参阅MSDN上的

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

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

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

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

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

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

我还制作了一个小视频,解释了基本的TPL,并演示了Parallel.For如何比普通任务和线程更有效地利用核心

实验1

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

for (int i = 0; i < 1000000000; i++)
{
    Task o = new Task(Method1);
    o.Start();
}
for(int i=0;i<100000000;i++)
{
任务o=新任务(方法1);
o、 Start();
}

在我看来,最现实的情况是任务需要完成繁重的操作。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; 
}
然后我用传统的方式叫了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()); 
List tasks=newlist();
for(int i=0;iSumRootN(10));
t、 Start();
任务。添加(t);
}
Task.WaitAll(tasks.ToArray());

第一个案例用26656ms完成,第二个案例用24478ms完成。我重复了很多次。每次第二种方法都稍微快一些。

IIRC,默认分区由Parallel.ForEach完成。ForEach还考虑了可用的硬件线程数,使您无需计算出启动的最佳任务数。请查看Microsoft的文章;里面对所有这些东西都有很好的解释。@Mal:有点……这实际上不是分区器,而是TaskScheduler的工作。TaskScheduler默认使用新的线程池,现在处理得很好。谢谢。我知道我应该留在“我不是专家,但是…”警告中。:)@ReedCopsey:如何将通过Parallel.ForEach启动的任务附加到包装器任务?因此,当您对包装器任务调用.Wait()时,它将挂起,直到并行运行的任务完成为止?@Tarkus如果您发出多个请求,最好在每个工作项(在并行循环中)中使用HttpClient.GetString。没有理由在已经并发的循环中添加异步选项,通常情况下……这样会更有效,而且创建线程的成本也更高。实验2是一个非常糟糕的做法。@Georgi It请关注更多关于什么是不好的内容。很抱歉,我的错误,我应该澄清一下。我的意思是在100000000的循环中创建任务。开销是无法想象的。更不用说并行程序一次不能创建超过63个任务,这使得它在这种情况下更加优化。100000000个任务也是如此。然而,当我处理一个图像(重复缩放分形)并并行处理时,许多内核在等待最后一个线程完成时处于空闲状态。为了加快速度,我自己将数据细分为64个工作包,并为其创建了任务。(然后Task.WaitAll等待完成。)这个想法是让空闲线程拿起一个工作包来帮助完成工作,而不是等待1-2个线程完成(Parallel.for)分配的块。在这个例子中,
Mehthod1()
做什么?使用Parallel.for是一种老式的方法。对于不统一的工作单元,建议使用任务。Microsoft MVP和TPL的设计者也提到,使用任务将更有效地使用线程,即在等待其他单元完成时不会阻塞那么多线程。
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());