I wrote a simple implementation of aysnc parallel.ForEach( ... )
All it really does is create a list of tasks and wait for them all to complete and aggregate the exceptions if any.
But to use CancellationToken I added Task.Run but I am worried that this is not the best way of stopping a task from running.
I could add some shortcuts in case the token cannot be cancelled, empty collections and so on.
But those are minor tweaks.
Anything else I could do to make tasks.AddRange(source.Select(s => Task.Run(() => body(s), token))); more efficient while allowing me to cancel running tasks.
public static async Task ForEach<T>(ICollection<T> source, Func<T, Task> body, CancellationToken token )
{
// create the list of tasks we will be running
var tasks = new List<Task>(source.Count);
try
{
// and add them all at once.
tasks.AddRange(source.Select(s => Task.Run(() => body(s), token)));
// execute it all.
await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false);
// throw if we are done here.
token.ThrowIfCancellationRequested();
}
catch
{
// find the error(s) that might have happened.
var errors = tasks.Where(tt => tt.IsFaulted).Select(tu => tu.Exception).ToList();
// we are back in our own thread
if (errors.Count > 0)
{
throw new AggregateException(errors);
}
}
}
Any suggestions where I could improve performance and the creation of tasks?