Task.WhenAll 的正确使用方法

10

我正在努力理解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();

    }
}

@SimonWhitehead - 我对async/await还不太熟悉,所以正在努力理解它。我猜“是否有更好的方法”才是正确的呢? - Sam
1
这通常是我实现 / 见过的方式。你需要等待一组任务异步完成。WhenAll 是我知道的通常做法。 - Simon Whitehead
2
在这种情况下,Task.WhenAll 的返回类型是实现了 IEnumerable<T>string[]results.Select(x => x) 什么也没做,只是增加了开销。这与 async-await 无关,它只是基本的 LINQ 和类型系统意识。 - Paulo Morgado
根据指南,您应该将方法命名为DoItAsync - Paulo Morgado
阅读我的策展上的文章,以了解更多信息。 - Paulo Morgado
1个回答

17

您当前代码存在的问题是,如果多个任务引发异常,则无法处理单个异常。

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

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是延迟评估的)。

更新,现在你可以通过消除 async/await 进一步优化 DoIt

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

但是,如果你这样做,请注意异常传播行为的变化


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接