The thread Nesting await in Parallel.ForEach has an answer suggested to use Task.WhenAll to run multiple (MaxDegreeOfParallelism) asynchronous tasks in parallel, not waiting until previous task is completed.
public static Task ForEachAsync<T>(
this IEnumerable<T> source, int dop, Func<T, Task> body)
{
return Task.WhenAll(
from partition in Partitioner.Create(source).GetPartitions(dop)
select Task.Run(async delegate {
using (partition)
while (partition.MoveNext())
await body(partition.Current).ContinueWith(t =>
{
//observe exceptions
});
}));
}
And call it like
ids.ForEachAsync(10, async id =>
{
ICustomerRepo repo = new CustomerRepo();
var cust = await repo.GetCustomer(id);
customers.Add(cust);
});
If body has a parameter, I want to know parameter value when handling exceptions.e.g. If task body failed for id , I need to log exception, specifying, that it happened for particular id.
I’ve looked at Accessing values in Task.ContinueWith, but wasn’t able to access parameters when t.IsFaulted.
Finally I’ve added try/catch inside lambda body and it seems to work
ids.ForEachAsync(10, async id =>
{
try
{
ICustomerRepo repo = new CustomerRepo();
var cust = await repo.GetCustomer(id);
customers.Add(cust);
}
catch(Exception e)
{
_logger.LogError(e,” id=“+ id);
}
});
However I am not sure, does it work correctly(i.e asynchronously, without blocking).
Later the author of the original answer suggested to use var current = partition.Current before await body and then use current in the continuation (ContinueWith(t => { ... }). –
Can anyone confirm, which approach is better? Any disadvantages of each of approaches?