【发布时间】:2011-04-17 16:08:37
【问题描述】:
我已经对这两个循环进行了试验,并注意到即使 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 结果传递给上述代码和观察者相同的行为:常规 foreach 似乎等待上一次迭代完成以开始下一次迭代,即使我使用了 .AsParallel() 和 .WithExecutionMode(ParallelExecutionMode.ForceParallelism)指令。
任何见解都将受到高度赞赏。 我知道 OrderingPartitioner 类,可以尝试使用它
【问题讨论】:
-
"即使 Task 的 Action 委托中的常规 foreach 循环应该并行执行" 您从哪里得到这个想法? 一个普通的foreach就是一个普通的foreach。里面没有花哨的平行东西。这就是为什么我们有
Parallel.ForEach()。 -
我比较了 Task 的 Action 委托和线性过程中的常规 foreach 性能。在 Task 的动作委托内部,它的执行速度更快,所以我假设线程池正在决定生成多少并行线程以使任务以最佳方式执行。
-
有多快?我敢打赌,这要么是纯粹的运气,要么是糟糕的基准测试。在任务中运行 foreach 仍然会按顺序运行,因为它仍然是 foreach。 没有魔法。您可以做的是与该 sequential foreach 并行运行。
-
速度没有翻倍,但明显快了很多。我会说 10 秒 vs 7。你可能是对的,这可能是你提到的那些原因之一。只是想确保,无论您如何实现 foreach,它仍然会等待上一次迭代,即使您循环通过 BlockingCollection(集合已准备好并行)
-
更新:使用 PLINQ 从 BlockingCollection 获取并行查询,并在生成的并行查询上使用常规 foreach 循环提供了最佳性能,并按顺序保存记录
标签: c# multithreading parallel-processing task-parallel-library