I write the code bellow and forgot to declare Main method as async, so my guess was that the producer
and consumer
tasks should run synchronously.
using System.Collections.Concurrent;
namespace MainProgram;
public class Program
{
public static void Main(string[] args)
{
using var intCollection = new BlockingCollection<int>();
int sum = 0;
Task.Run(() =>
{
Console.WriteLine("Running Producer");
Thread.Sleep(1000);
for (int i = 0; i < 10; i++)
{
intCollection.Add(i);
Console.WriteLine(i);
}
intCollection.CompleteAdding();
});
Task.Run(() =>
{
Console.WriteLine("Running Consumer");
Thread.Sleep(1000);
while (!intCollection.IsCompleted)
{
sum += intCollection.Take();
Console.WriteLine("Thingi Thingi");
}
});
Console.WriteLine("aaabbb");
Console.ReadLine();
}
}
But the output of this code looks like this:
aaabbb
Running Producer
Running Consumer
0
Thingi Thingi
1
Thingi Thingi
2
Thingi Thingi
3
Thingi Thingi
4
Thingi Thingi
5
Thingi Thingi
6
Thingi Thingi
7
8
9
Thingi Thingi
Thingi Thingi
Thingi Thingi
This is to my surprise, because the tasks appear to run asynchronously. Can someone explain this to me?
The Task.Run
method invokes the action
delegate on the ThreadPool
. So after calling twice this method, the ThreadPool
spawned 2 threads to handle the request for work. At that point your program had at least 3 threads alive: the main thread of the process, and 2 background threads owned by the ThreadPool
. Seeing the two background threads running concurrently is the expected behavior. Whether the Main
method is async
is irrelevant.
In case you had invoked the Task.Run
100 times, the ThreadPool
wouldn't have spawned 100 threads instantly to satisfy the demand. You can find a description of ThreadPool
's algorithm that creates and destroys threads here or here. It is not very complicated. It involves a threshold that is equal to the number of CPU cores in your machine, and is adjustable with the SetMinThreads
API.