C# – Calling multiple async services in parallel

asyncasynchronous-programmingctask

I have few async REST services which are not dependent on each other. That is while "awaiting" a response from Service1, I can call Service2, Service3 and so on.

For example, refer below code:

var service1Response = await HttpService1Async();
var service2Response = await HttpService2Async();

// Use service1Response and service2Response

Now, service2Response is not dependent on service1Response and they can be fetched independently. Hence, there is no need for me to await response of first service to call the second service.

I do not think I can use Parallel.ForEach here since it is not CPU bound operation.

In order to call these two operations in parallel, can I call use Task.WhenAll? One issue I see using Task.WhenAll is that it does not return results. To fetch the result can I call task.Result after calling Task.WhenAll, since all tasks are already completed and all I need to fetch us response?

Sample Code:

var task1 = HttpService1Async();
var task2 = HttpService2Async();

await Task.WhenAll(task1, task2)

var result1 = task1.Result;
var result2 = task2.Result;

// Use result1 and result2

Is this code better than the first one in terms of performance? Any other approach I can use?

Best Answer

One issue I see using Task.WhenAll is that it does not return results

But it does return the results. They'll all be in an array of a common type, so it's not always useful to use the results in that you need to find the item in the array that corresponds to the Task that you want the result for, and potentially cast it to its actual type, so it might not be the easiest/most readable approach in this context, but when you just want to have all of the results from every task, and the common type is the type you want to treat them as, then it's great.

To fetch the result can I call task.Result after calling Task.WhenAll, since all tasks are already completed and all I need to fetch us response?

Yes, you could do that. You could also await them (await would unwrap the exception in any faulted task, whereas Result would throw an aggregate exception, but otherwise it'd be the same).

Is this code better than the first one in terms of performance?

It performs the two operations at the same time, rather than one and then the other. Whether that's better or worse is dependant on what those underlying operations are. If the underlying operations are "read a file from disk" then doing them in parallel is likely slower, as there is only one disk head and it can only be in one place at any given time; it jumping around between two files will be slower than reading one file then another. On the other hand, if the operations are "perform some network request" (as is the case here) then they'll very likely be faster (at least up to a certain number of concurrent requests), because you can wait for a response from some other network computer just as fast when there is also some other pending network request going on. If you want to know if it's faster in your situation, test it.

Any other approach I can use?

If it's not important to you that you know all of the exceptions thrown among all of the operations you're doing in parallel rather than just the first one, you can simply await the tasks without WhenAll at all. The only thing WhenAll gives you is having an AggregateException with every single exception from every faulted task, rather than throwing when you hit the first faulted task. It's as simple as:

var task1 = HttpService1Async();
var task2 = HttpService2Async();

var result1 = await task1;
var result2 = await task2;