Parallel.ForEach and foreach loops for BlockingCollection.GetConsumingEnumerable() collection in TaskFactory.Tasks
I have experimented with both of these loops and came to notice that even though regular foreach loop in Task's Action delegate supposed to perform in parallel, it doesn't process elements in parallel. However if i replace it with Parallel.ForEach i see data is being processed in parallel across multiple threads.
Code 1:
Task loadingTask1 = Factory.StartNew(() =>
{
foreach (MyOneClass dg in Queue.GetConsumingEnumerable())
{
MyOtherClass vl = new MyOtherClass();
vl.Id = dg.Id;
vl.PerformTimeConsumingAction();
OutputQueue.Add(vl);
}
});
Code 2:
Task loadingTask2 = Factory.StartNew(() =>
{
Parallel.ForEach(Queue.GetConsumingEnumerable(), (dg) =>
{
MyOtherClass vl = new MyOtherClass();
vl.Id = dg.Id;
vl.PerformTimeConsumingAction();
OutputQueue.Add(vl);
});
});
Code 1 when run with Console.Write statement on each iteration seems to be waiting for the previous cycle to complete till it grabs next one, but Code 2 does process multiple elements in parallel.
Am i not understanding regular foreach in Task.Action correctly? I thought .NET would start as many threads for the task as load warrants and each iteration of foreach would be processed in parallel.
I have also tried passing PLINQ result to both of the above codes and observer same behavior: regular foreach seemed to wait for the previous iteration to complete to start the next one, even though i have used .AsParallel()
and .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
directives.
Any insight would be highly appreciated. I am aware of OrderingPartitioner开发者_运维知识库 class and may try using it
A regular foreach always runs its iterations sequentially. There is no magic that turns it into a parallel construct in some situations. That would be like throwing you into the pit of despair, because then it would be difficult to assert the correctness of something as simple as a foreach loop. Fortunately, one of the goals of C# is to throw you into the pit of success:
If you put a foreach loop running on a separate task, you get all the iterations running sequentially, but you can run other code in parallel with the entire foreach.
The flow of execution of a regular foreach on a separate task looks like this:
|
__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
And the flow of execution of a Parallel.Foreach
looks like this:
|
_________________v________________
/ / / / \ \ \ \
|1 |2 |3 |....| |n-2 |n-1 |n
\____\____\____\____/____/____/____/
|
v
Hope that helps understand what's happening.
精彩评论