I thought I had a decent grasp of async/await in C#, but this code I am working on has me questioning what is happening behind the scenes.
I have a sync method that takes a bit of time to run, and I have a loop where I need to call this method anywhere from 500-2500 times. I basically want to call the method async so I can fire off all of the method calls and then pick up the results later.
This works as I would expect:
public async Task SomeMethod(List<Foo> fooList)
{
var taskList = new List<Task<MyData>>();
foreach (var foo in fooList)
{
taskList.Add(Task.Run(() => LongRunningMethod(foo)));
}
// do other stuff
await Task.WhenAll(taskList);
}
public MyData LongRunningMethod(Foo foo)
{
Thread.Sleep(10000);
return new MyData();
}
What I see happen - all of the tasks are added to the taskList very quickly, then I see all of the LongRunningMethod calls executing on a variety of threads, taking 10 seconds, and completing. When the first method hits the await Task.WhenAll, it sits there until all of the LongRunningMethod calls are complete.
However, as I was messing around I tried this iteration:
public async Task SomeMethod(List<Foo> fooList)
{
var taskList = new List<Task<MyData>>();
foreach (var foo in fooList)
{
taskList.Add(LongRunningMethod(foo));
}
// do other stuff
await Task.WhenAll(taskList);
}
public async Task<MyData> LongRunningMethod(Foo foo)
{
await Task.Delay(10000);
return new MyData();
}
The behavior is very different. Here, I see all the tasks added very quickly, but they are all on the same thread, and there is only about a 10 second delay overall before the await Task.WhenAll completes. It seems like each individual task executes to completion instantly, where I would expect it to run like the first interation where each task is taking 10 seconds to run.
What is the difference between how these two iterations work behind the scenes, and which is the proper way to accomplish my goal (firing off all of the LongRunningMethod calls, ideally have them all processing simultaneously to speed things up, and waiting for them all to finish before I move on)?
Edit to add details requested by Damien: This is a .Net 6 worker service (so there is no synchronization context, correct?) The goal is to try and cut down the amount of time this process takes to run by kicking off multiple LongRunningMethod calls at once instead of waiting for each one to complete before sending the next one.