Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper use of Task.WhenAll

Tags:

c#

async-await

I am trying to wrap my head around async/await and wanted to know if this is the proper use of the Task.WhenAll method:

public class AsyncLib
{
    public async Task<IEnumerable<string>> DoIt()
    {
        var urls = new string[] { "http://www.msn.com", "http://www.google.com" };

        var tasks = urls.Select(x => this.GetUrlContents(x));

        var results = await Task.WhenAll(tasks);

        return results.Select(x => x);
    }

    public async Task<string> GetUrlContents(string url)
    {
        using (var client = new WebClient())
        {
            return await client.DownloadStringTaskAsync(url);
        }
    }
}

Main

This is the calling console application.

class Program
{
    static void Main(string[] args)
    {
        var lib = new AsyncLib();
        foreach(var item in lib.DoIt().Result)
        {
            Console.WriteLine(item.Length);
        }
        Console.Read();

    }
}
like image 694
Sam Avatar asked May 13 '14 01:05

Sam


People also ask

What does await task WhenAll () do?

WhenAll(IEnumerable<Task>)Creates a task that will complete when all of the Task objects in an enumerable collection have completed.

What is the difference between task WaitAll and task WhenAll?

The Task. WaitAll blocks the current thread until all other tasks have completed execution. The Task. WhenAll method is used to create a task that will complete if and only if all the other tasks have completed.

Can you tell difference between task WhenAll and task WhenAny?

WhenAll returns control after all tasks are completed, while WhenAny returns control as soon as a single task is completed.

What is difference between WaitAll and WhenAll?

The big difference between them is that WaitAll return a Void (it blocks the current thread) instead, WhenAll, return a Task (we can decide to block or not the current thread).


1 Answers

The problem with your current code is that you won't be able to handle individual exceptions, if more than one task throws.

If this is a concern, then with the following approach, you can handle them:

public async Task<Task<string>[]> DoIt()
{
    var urls = new string[] { "http://www.msn.com", "http://www.google.com" };

    var tasks = urls.Select(x => this.GetUrlContents(x)).ToArray();

    await Task.WhenAll(tasks);

    return tasks;
}

// ...

static void Main(string[] args)
{
    var lib = new AsyncLib();
    foreach(var item in lib.DoIt().Result)
    {
        Console.WriteLine(item.Result.Length);
    }
    Console.Read();

}

Note I use ToArray() to avoid evaluating the enumerable and starting the tasks for more than once (as LINQ is lazy-evaluated).

Updated, now you can further optimize DoIt by eliminating async/await:

public Task<Task<string>[]> DoIt()
{
    var urls = new string[] { "http://www.msn.com", "http://www.google.com" };

    var tasks = urls.Select(x => this.GetUrlContents(x)).ToArray();

    return Task.Factory.ContinueWhenAll(
        tasks, 
        _ => tasks, 
        CancellationToken.None, 
        TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

However, if you do so, be aware of the change in the exception propagation behavior.

like image 111
noseratio Avatar answered Oct 16 '22 04:10

noseratio