问题描述
|
我已经对这两个循环进行了实验,发现即使Task \的Action委托中的常规foreach循环应该并行执行,也不会并行处理元素。但是,如果我用Parallel.ForEach替换它,我看到数据正在跨多个线程并行处理。
代码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);
});
});
在每次迭代中使用Console.Write语句运行时,代码1似乎正在等待上一个周期完成,直到抓住下一个周期为止,但是代码2确实并行处理多个元素。
我是否无法正确理解Task.Action中的常规foreach?我以为.NET将按照负载保证启动尽可能多的线程来执行任务,并且foreach的每次迭代都将并行处理。
我也尝试过将PLINQ结果传递给上述代码,并且观察者具有相同的行为:即使我使用了.AsParallel()
和.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
指令,常规的foreach似乎也在等待上一个迭代完成以开始下一个迭代。
任何见解将不胜感激。
我知道OrderingPartitioner类,可以尝试使用它
解决方法
常规的foreach始终按顺序运行其迭代。在某些情况下,没有任何魔术可以将其变成并行构造。这就像让您陷入绝望之地,因为那样很难断言诸如foreach循环之类的简单事物的正确性。幸运的是,C#的目标之一就是将您带入成功之路:
如果将foreach循环放在单独的任务上运行,则会使所有迭代顺序运行,但是可以将其他代码与整个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
希望能帮助您了解正在发生的事情。