Speedup multiple webservice calls with Task.WhenAll or Parallel.Invoke

6/26/2018 Jeff

On a recent project, we needed to pull in a significant amount of data from the database, all from various webservices. This is a classic case for concurrent processing. Two techniques are: Task.WhenAll() and Parellel.Invoke()

First (wrong) attempt: Using Task.WaitAll:

For my first attempt at this, I tried the following:

public ResultData GetResults(int id)
{
    var result = new ResultData();
    var group1DataTask = getGroup1DataAsync(id);
    var group2DataTask = getGroup2DataAsync(id);
    var group3DataTask = getGroup3DataAsync(id);
    // WARNING, DON'T DO THIS:
    Task.WaitAll(group1DataTask , getGroup2DataTask , getGroup3DataTask );

    result.Group1 = group1DataTask.Result;
    result.Group2 = group2DataTask.Result;
    result.Group3 = group3DataTask.Result;
    return resultData;
}

Task.WaitAll seemed like a good option. The problem here is that both .WaitAll and .Result are blocking operations. In this implementation, it was leading to deadlocks. Note that I have found cases where Task.WaitAll does work properly though. See here.

A better approach: Using Task.WhenAll:

First, convert this function to async, then use await Task.WhenAll.

public async ResultData GetResults(int id)
{
    var result = new ResultData();
    var group1DataTask = getGroup1DataAsync(id);
    var group2DataTask = getGroup2DataAsync(id);
    var group3DataTask = getGroup3DataAsync(id);
    // A better solution:
    await Task.WhenAll(group1DataTask , getGroup2DataTask , getGroup3DataTask );

    result.Group1 = group1DataTask.Result;
    result.Group2 = group2DataTask.Result;
    result.Group3 = group3DataTask.Result;
    return resultData;
}

This approach does not use .WaitAll and .Result calls. This is a safer approach on UI and ASP.NET applications. Notice that 'await' can now be added to the WhenAll call, and after the call I used await group1DataTask instead of group1DataTask.Result.

Remember: await is asynchronous while .Wait, .WaitAll & .Result block the current thread.

WaitAll vs WhenAll

The primary differences are that WaitAll is a function that returns void, and blocks the current thread until all are complete. On a UI or ASP.NET, these can lead to deadlocks. WhenAll returns a task that can be awaited. This has a few advantages in that first off, you can keep the UI from blocking by doing the following:

await Task.WhenAll(...);

Plus, you can do anything else that can be done with Task objects, such as:

Task.WhenAll(...).ContinueWith(t => ... );

How about neither??

It's worth noting that the following does produce concurrency:

public ResultData GetResults(int id)
{
    var result = new ResultData();
    var group1DataTask = getGroup1DataAsync(id);
    var group2DataTask = getGroup2DataAsync(id);
    var group3DataTask = getGroup3DataAsync(id);
 
    result.Group1 = await group1DataTask;
    result.Group2 = await group2DataTask;
    result.Group3 = await group3DataTask;
    return resultData;
}

Each call does get kicked off in a concurrent fashion, and the return line isn't hit until all three awaits complete, making this a simple solution. The biggest disadvantages to this approach are:

  1. It doesn't allow for partial success. Task.WhenAll will wait for all to complete, even if one throws an exception.
  2. It doesn't let you log all thrown exceptions, only the first one

Using Parallel.Invoke()

Parallel.Invoke is another means of performing concurrency. Here's a simple example of using Parallel.Invoke:

public ResultData GetResults(int id)
{
    var result = new ResultData();
    Parallel.Invoke(
        () => result.GroupA = getGroupAData(id),
        () => result.GroupB = getGroupBData(id),
        () => result.GroupC = getGroupCData(id)
    );

    return resultData;
}

This is mentioned only to introduce another option for making parallel calls. Your mileage on this may vary based on your application. A full discussion on Parallel.Invoke will have to wait for another day...


Please register or login to add a comment.

Comments (displaying 1 - 1):
No comments yet! Be the first...


  • C#/.NET
  • T-SQL
  • HTML/CSS
  • JavaScript/jQuery
  • .NET 8
  • ASP.NET/MVC
  • Xamarin/MAUI
  • WPF
  • Windows 11
  • SQL Server 20xx
  • Android
  • XBox
  • Arduino
  • Skiing
  • Rock Climbing
  • White water kayaking
  • Road Biking