I am aiming to improve my understanding regarding Concurrency in C# and ran into a question with a small toy problem I have.
Let’s consider an async method async Task<int> CountAsync(int id)
that counts something based on a given ID, but for doing so, it has to make a web request for example, thus being async.
Now I would like to parallelize counting multiple different IDs. I asumed that that not awaiting each CountAsync
call in a loop should do the job.
var countTasks = new List<Task<int>>();
for (int i = 1; i <= upUntil; i++)
{
countTasks.Add(CountAsync(i));
}
await Task.WhenAll(countTasks);
var count = countTasks.Select(x => x.Result).Sum();
However, it shows that this is as fast, as simply calling and awaiting each Count inside the loop.
To get the counting to be actually done in parallel, I have to make use of Task.Run()
and write my code like this:
var countTasks = new List<Task<int>>();
for (int i = 1; i <= upUntilPasture ; i++)
{
countTasks.Add(Task.Run(async () => await Pastures.CountAsync(i)));
}
await Task.WhenAll(countTasks);
var count = countTasks.Select(x => x.Result).Sum();
Or use the alternative with AsParallel()
to get some more control over the degree of parallelism.
count = Enumerable.Range(1, upUntil)
.AsParallel()
.WithDegreeOfParallelism(16)
.Select(CountAsync)
.Sum(t => t.Result);
Reading “Concurrency in C#” by Stephen Cleary made me aware of parallelism and asynchronous methods being different and the fact that I am actually in a parallel processing scenario here. However, I would have assumed that not awaiting CountAsync in my first loop and simply collecting the Promises of the async method, would also lead to a speedup in executing, as it can be offloaded to a different thread.
I obviously lack understanding here and would like to figure out what happens behind the scenes in my first example. Also: are my other code examples the way to go when having a parallel operation with async methods? I do not like the use of .Result.