3

我已经尝试过这两个循环,并发现即使在Task的Action委托中的常规foreach循环应该并行执行,它也不会并行处理元素。但是,如果我用Parallel.ForEach替换它,我看到跨多个线程并行处理数据。Parallel.ForEach和Foreach循环的TaskFactory.Tasks中BlockingCollection.GetConsumingEnumerable()集合

代码1:

Task loadingTask1 = Factory.StartNew(() => 
     { 
      foreach (MyOneClass dg in Queue.GetConsumingEnumerable()) 
      { 

       MyOtherClass vl = new MyOtherClass(); 
       vl.Id = dg.Id; 
       vl.PerformTimeConsumingAction(); 

       OutputQueue.Add(vl); 
      } 
     }); 

代码2:

Task loadingTask2 = Factory.StartNew(() => 
     { 
      Parallel.ForEach(Queue.GetConsumingEnumerable(), (dg) => 
      { 

       MyOtherClass vl = new MyOtherClass(); 
       vl.Id = dg.Id; 
       vl.PerformTimeConsumingAction(); 

       OutputQueue.Add(vl); 
      }); 
     }); 

代码1时Console.Write声明运行在每次迭代似乎在等待前一个周期完成下一个,直到它抓住一个,但Code 2并行处理多个元素。

我是不是无法正确理解Task.Action中的常规foreach?我认为.NET会像负载保证一样为任务启动多个线程,并且每个foreach迭代将被并行处理。

我也试过将PLINQ结果传递给上面的代码和观察者的相同行为:即使我已经使用了.AsParallel().WithExecutionMode(ParallelExecutionMode.ForceParallelism)指令,常规foreach似乎等待先前的迭代完成以启动下一个代码。

任何有识之士将不胜感激。 我知道OrderingPartitioner类,并可能尝试使用它

+0

“尽管Task's Action委托中的常规foreach循环应该并行执行”您是从哪里得到这个想法的? **一个普通的foreach是一个普通的foreach **。没有花哨的东西在里面。这就是为什么我们有'Parallel.ForEach()'。 – 2011-04-17 16:10:28

+0

我已经比较了Task的Action委托和线性过程中的常规foreach性能。内部任务的动作委托它执行得更快,所以我认为threadpool正在决定要产生多少个并行线程来执行最佳任务。 – Dimitri 2011-04-17 16:15:28

+0

有多快?我敢打赌,这是纯粹的运气或糟糕的基准。在任务内运行foreach仍然按顺序运行它,因为它仍然是一个foreach。 **没有魔法**。你可以做的是与* sequential * foreach并行运行。 – 2011-04-17 16:16:19

回答

16

有规律的foreach 总是顺序运行其迭代。在某些情况下,没有什么魔法可以将它变成平行结构。这就像把你扔进pit of despair,因为那样很难断言像foreach循环那样简单的事情的正确性。幸运的是,C#的目标之一就是你投入到成功的坑

C# throwing you into the pit of success

如果你把一个单独的任务运行的foreach循环,你会得到所有的迭代运行顺序,但你可以与其他代码并行运行其他代码。

一个普通的foreach的执行上一个单独的任务的流程是这样的:

   | 
      __v_ 
     / \ 
other code | | foreach iteration 1 
other code | | foreach iteration 2 
other code | | foreach iteration 3 
      ...... 
other code | | foreach iteration n-1 
other code | | foreach iteration n 
      v v 

而一个Parallel.Foreach的执行流程是这样的:

    | 
_________________v________________ 
//// \ \ \ \ 
|1 |2 |3 |....| |n-2 |n-1 |n 
\____\____\____\____/____/____/____/ 
        | 
        v 

希望帮助理解发生了什么。

+0

真棒绘图! :)感谢您花时间和解释 – Dimitri 2011-04-17 16:41:26

+0

@Dimitri:这是来自Jon Skeet的博客:http://msmvps.com/blogs/jon_skeet/archive/2010/09/02/presentation-preparation.aspx。 – 2011-04-17 16:42:45

+0

现在有没有办法保证使用Parallel.ForEach进行BlockingCollection的先进先出处理?我想我必须实现自定义分区程序? – Dimitri 2011-04-17 16:51:27