0

Assuming we have a List of filters, is it ok to use Parallel.ForEach to make async calls and then use them? Must we have to wait for all calls to end? What if we have 3 async calls and one fails/times out?

var results = new ConcurrentBag<Service>();

Parallel.ForEach(filters, async filter =>
{
   var result = await serviceClient.GetAllAsync(filter).ConfigureAwait(false);

   results.AddRange(result);
});

MyMapper.Map(results);

where MyMapper has a method:

Map(IEnumerable<Service> services) {
   foreach(Service service in services) {
      //do stuff
   }
}
Junior Mayhé
  • 16,144
  • 26
  • 115
  • 161

2 Answers2

2

The Parallel library is thought to process millions of items in parallel. You won't get any improvement using Parallel.ForEach there. You could use Task.WhenAll instead.

var tasks = new List<Task>();

foreach(var filter in filters)
{
   tasks.Add(serviceClient.GetAllAsync(filter));
}

results.AddRange(await Task.WhenAll(tasks).ConfigureAwait(false));

You could do some LinQ. I didn't so the idea is more clear. With this code you could have some degree of parallelism and the await will help you with exceptions.

hardkoded
  • 18,915
  • 3
  • 52
  • 64
  • 1
    What is the point of `results.Add(await task);`? You are awaiting completed task. Just get the result of `Task.WhenAll` – FCin Dec 24 '18 at 13:24
  • @FCin Not my code sample though. I have been doing some code review. I noticed we have some `await Task.WhenAll` in many parts of our solution. :-( – Junior Mayhé Dec 24 '18 at 15:49
  • OP will gain a performance improvement by executing service calls in parallel. But, executing it on different cores will waste threads for doing nothing (because I assume service call access some external resource), those threads will only waiting for a response. Where async-await provide almost "parallel" approach by using only one thread. – Fabio Dec 25 '18 at 02:38
-3

I would do that a bit differently. Assuming that

serviceClient.GetAllAsync(filter)

is doing something in different thread/task or does a request on some url, I dont see the need for Paralle.ForEach usage.

What I would do is create a list of Tasks and invoke them using Task.WaitAll method. This way you can handle any exceptions for faulted tasks.

Code snippet for this would be something similar to this:

  var queryService = filters.Select(x => sClient.GetAllAsync()).ToArray();

        try
        {
            // Wait for all the tasks to finish.
            Task.WaitAll(queryService);
        }
        catch (AggregateException e)
        {
            //At least one of the Task instances was canceled.
            //If a task was canceled, the AggregateException contains an OperationCanceledException in its InnerExceptions collection.
        }
Bola
  • 718
  • 1
  • 6
  • 20