I observed a strange behavior while experimenting with the Parallel.ForEach
method and the BlockingCollection<T>
class. Apparently calling the two lines below on a separate thread, is enough to cause an ever increasing number of threads in the ThreadPool
:
var queue = new BlockingCollection<int>();
Parallel.ForEach(queue.GetConsumingEnumerable(), _ => { });
The queue
contains no elements. I was expecting that the Parallel.ForEach
loop would be idle, waiting for items to be added in the queue
. Apparently it's not idle, because the ThreadPool.ThreadCount
is increasing by one every second. Here is a minimal demo:
public class Program
{
public static void Main()
{
new Thread(() =>
{
var queue = new BlockingCollection<int>();
Parallel.ForEach(queue.GetConsumingEnumerable(), _ => { });
})
{ IsBackground = true }.Start();
Stopwatch stopwatch = Stopwatch.StartNew();
while (true)
{
Console.WriteLine($"ThreadCount: {ThreadPool.ThreadCount}");
if (stopwatch.ElapsedMilliseconds > 8000) break;
Thread.Sleep(1000);
}
Console.WriteLine("Finished");
}
}
Output:
ThreadCount: 0
ThreadCount: 4
ThreadCount: 5
ThreadCount: 6
ThreadCount: 7
ThreadCount: 8
ThreadCount: 10
ThreadCount: 11
ThreadCount: 12
Finished
Can anyone explain why is this happening, and how to prevent it from happening? Ideally I would like the Parallel.ForEach
to consume at most one ThreadPool
thread, while the queue
is empty.
I am searching for a solution applicable on .NET Core 3.1 and later.