正确使用Task.WhenAll

时间:2014-05-13 01:05:05

标签: c# async-await

我试图围绕async / await,并想知道这是否正确使用Task.WhenAll方法:

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);
        }
    }
}

主要

这是调用控制台应用程序。

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

    }
}

1 个答案:

答案 0 :(得分:15)

您当前代码的问题在于,如果有多个任务抛出,您将无法处理个别异常。

如果这是一个问题,那么使用以下方法,您可以处理它们:

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();

}

注意我使用ToArray()来避免评估可枚举和多次启动任务(因为LINQ是惰性求值的)。

已更新,现在您可以通过取消DoIt来进一步优化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);
}

但是,如果您这样做,请注意exception propagation behavior

中的更改