I have a list of tasks that I want to execute in parallel using Parallel.ForEach. It starts fine with 4 tasks running in parallel but in the end it decreases to only one task at a time. Here is the count of parallel tasks in time:
1 2 3 4 4 3 4 4 ... 4 4 4 3 3 1 1 1 1 1 1 1
Max degree of parallelism is set to 4. At the end of execution only one task is executed at one time and all executions run on the same thread. My question is why I am getting this one task at a time execution in the end? How can I avoid this?
Here is the code:
var threadCount = 4;
ThreadPool.SetMinThreads(threadCount, threadCount);
Parallel.ForEach(taskDataList,
new ParallelOptions() {MaxDegreeOfParallelism = threadCount},
(x) => { RunOne(x); });
RunOne function starts external process and waits for it to end. Some suspected that RunOne could have been the problem of lack of parallel execution. To make sure that this is not the case I recreated situation by replacing this function with a sleep call of identical duration. The code is below. Here t is the list of seconds each task takes. activeCount is the number of currently running tasks and remaining is the number of tasks that still remain in the list.
var t = new List<int>()
{2,2,2,1,1,1,1,1,1,1,
1,1,1,1,1,3,1,1,1,1,
1,1,1,1,1,1,1,1,5,4,
26,12,11,16,44,4,37,26,13,36};
int activeCount = 0;
int remaining = t.Count;
Parallel.ForEach(t, new ParallelOptions() {MaxDegreeOfParallelism = 4},
(x) =>
{
Console.WriteLine($"Active={Interlocked.Increment(ref activeCount)}"+
$"Remaining={Interlocked.Decrement(ref remaining)} " +
$"Run thread={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(x * 1000); //Sleep x seconds
Interlocked.Decrement(ref activeCount);
});
At the very end it produces output like this:
Active=2 Remaining=7 Run thread=3
Active=1 Remaining=6 Run thread=3
Active=1 Remaining=5 Run thread=3
Active=1 Remaining=4 Run thread=3
Active=1 Remaining=3 Run thread=3
Active=1 Remaining=2 Run thread=3
Active=1 Remaining=1 Run thread=3
Active=1 Remaining=0 Run thread=3
This output shows that in the end only 1 task is running when 6 tasks still remain. With limit of 4 parallel tasks it does not make any sense. When 6 tasks are still available I would expect to see 4 tasks running in parallel.
Should I use Parallel.ForEach differently or is it a bug/feature?