I am trying to understand threading better and I have run into something that is confusing me. As far as I know Task.Run() starts the task in another thread.
I built some code below to test it out to see how it behaves but there is a hole in my understanding.
I imagined that I could start tasks in a loop like this:
public void DoTheThings(List<string> inputList)
{
List<Task> taskList = new List<Task>();
foreach (var input in inputList)
{
taskList.Add(Task.Run(() => this.GetTheStuff(input)));
}
Task.WaitAll(taskList.ToArray());
Console.WriteLine("Queue completed");
}
And if the called task (GetTheStuff()) had a delay in it then this would lock that thread so the next started task would be in a new thread:
public async Task GetTheStuff(string input)
{
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + "starting");
int delay = GetRandomNumber(1000, 5000); // simulate time of a http request or something similar
var notInUse = input; // in real app this would be some useful assignment
await Task.Delay(delay);
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + "ending");
}
But this doesn't happen. The same threads are used to start multiple tasks. Or so it seems by looking at the "ManagedThreadID" at the start and end of function.
In my erroneius assumption I thought that the Main() function would be a thread. It would launch a new thread for DoTheThings() and then this function would launch multiple threads for concurrent GetTheStuff() processing.
What is actually happening?
Complete code:
class Program
{
private static void Main(string[] args)
{
// build list of 100 random strings to represent input
List<string> thingsToProcess = new List<string>();
for (int i = 0; i < 100; i++)
{
thingsToProcess.Add(Path.GetRandomFileName());
}
Console.WriteLine("Starting queue");
var m = new MethodStuff();
var mainTask = Task.Run(() => m.DoTheThings(thingsToProcess));
Task.WaitAll(mainTask);
Console.WriteLine("All done");
Console.ReadLine();
}
}
class MethodStuff
{
private static readonly Random getrandom = new Random();
private static readonly object syncLock = new object();
public static int GetRandomNumber(int min, int max)
{
lock (syncLock)
{ // synchronize
return getrandom.Next(min, max);
}
}
// loop over all input and start each input in its own thread
public void DoTheThings(List<string> inputList)
{
List<Task> taskList = new List<Task>();
foreach (var input in inputList)
{
taskList.Add(Task.Run(() => this.GetTheStuff(input)));
}
Task.WaitAll(taskList.ToArray());
Console.WriteLine("Queue completed");
}
public async Task GetTheStuff(string input)
{
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + "starting");
int delay = GetRandomNumber(1000, 5000); // simulate time of a http request or something similar
var notInUse = input; // in real app this would be some useful assignment
await Task.Delay(delay);
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + "ending");
}
}