original
foreach (file in files)
{
ReadTheFileAndComputeAFewThings(file);
CallAWebService(file);
MakeAFewDbCalls(file);
}
original + async (better than above, depending!)
foreach (file in files)
{
await ReadTheFileAndComputeAFewThings(file);
await CallAWebService(file);
await MakeAFewDbCalls(file);
}
This will not be better if the calls are not actually implementing async , then it will be worse.
Another way this will be worse is if the async-ness is so short they it out weight the cost of Task.
Each async Task, creates a managed thread, which reverse 1mb from system, and add thread syncing time.
Altho the syncing is extremely low if this is done in a tight loop it will see performance issues.
Key here is the Task must actually be the async versions.
Parallel (better than above, depending!)
Parallel.ForEach(files, item)
{
ReadTheFileAndComputeAFewThings(item);
CallAWebService(item);
MakeAFewDbCalls(item);
}
If this can all happen at the same time, then this is better.
Also only if you want to assign multiple thread, resources, remember resources are limited, you machine only has so many cores and ram, you would want to manage this depending on what else the hardware is responsible for.
Not better if the methods are not thread safe.
Parallel + async (better than above, depending!)
Parallel.ForEach(files, item)
{
await ReadTheFileAndComputeAFewThings(item);
await CallAWebService(item);
await MakeAFewDbCalls(item);
}
FYI - Parallel + async example above is actually incorrect!!!
As the Parallel.ForEach itself is not async, you will need to do some research as to how to build a async version of Parallel.ForEach
Also the same comments above apply when using in conjunction.
Update
based on a comment it largly depend on whether ConfigureAwait() has been set,
but assuming you haven't then. Also this will not excute in order so if CallAWebService depends on ReadTheFileAndComputeAFewThings then things will probably do wrong.
foreach (file in files)
{
List<Task> jobs = new List<Task>();
jobs.Add(ReadTheFileAndComputeAFewThings(file))
jobs.Add(CallAWebService(file))
jobs.Add(MakeAFewDbCalls(file))
Task.WhenAll(jobs.ToArray());
}
or...
List<Task> jobs = new List<Task>();
foreach (file in files)
{
jobs .Add(ReadTheFileAndComputeAFewThings(file))
jobs .Add(CallAWebService(file))
jobs .Add(MakeAFewDbCalls(file))
}
Task.WhenAll(jobs.ToArray());
difference between the two is the the one has a lot more tasks, and you probably run into issues with the later regarding context.... aka the enumerator will no longer have the correct "index" to file and if the one call had a dependency on the other being completed first.
Amazing link explaining async...
https://learn.microsoft.com/en-us/archive/blogs/benwilli/tasks-are-still-not-threads-and-async-is-not-parallel