异步方法的并发执行

6
使用async/await模型,我有一个方法会调用3个不同的Web服务,然后返回结果的并集。
var result1 = await myService.GetData(source1);
var result2 = await myService.GetData(source2);
var result3 = await myService.GetData(source3);

allResults = Union(result1, result2, result3);

使用常规的 await,这三个调用将会按照互相之间同步执行。我应该如何让它们并发执行并在完成时合并结果呢?


GetData 方法的返回类型是什么? - Yurii
无所谓。流、字符串、字节数组… - earthling
1
不要立即等待它们,将返回的值存储在数组或列表中,并使用 await Task.WhenAll(...); - Dirk
是的,我刚找到一篇文章,简要介绍了WhenAll的相关内容。链接为http://msdn.microsoft.com/en-us/magazine/jj991977.aspx。 - earthling
3个回答

10

我该如何让它们并行执行并在它们完成时合并结果呢?

最简单的方法就是创建所有任务,然后等待它们完成:

var task1 = myService.GetData(source1);
var task2 = myService.GetData(source2);
var task3 = myService.GetData(source3);

// Now everything's started, we can await them
var result1 = await task1;
var result1 = await task2;
var result1 = await task3;

你还可以考虑使用Task.WhenAll。需要考虑一个任务失败的可能性,如果使用上述代码,例如当task2失败时,你将无法观察到task3的失败,因为异步方法会在等待task3之前传播task2的异常。

我并不推荐一种具体的策略,因为这取决于你的具体情况。你可能只关心成功或失败,并记录一个失败原因,在这种情况下,上面的代码是可以的。否则,你可以潜在地附加延续到原始任务以记录所有异常。


谢谢Jon。我刚刚了解到WhenAll。我会研究一下如何使用它。顺便说一句,我假设在代码示例中,您打算从方法调用中删除awaits,对吗? - earthling
1
如果你使用await Task.WhenAll(通常情况下),它不会抛出AggregateException。相反,它将会(重新)抛出故障任务中的一个异常。 - Stephen Cleary

0
作为一种更通用的解决方案,您可以使用我下面编写的API,它还允许您定义最大并发异步请求的实时限流机制。
inputEnumerable将是您源代码的可枚举对象,asyncProcessor是您的异步委托(在您的示例中为myservice.GetData)。
如果asyncProcessor - myservice.GetData - 返回void或仅返回没有任何类型的Task,则您可以简单地更新API以反映这一点。(只需将所有Task<>引用替换为Task即可)
    public static async Task<TOut[]> ForEachAsync<TIn, TOut>(
        IEnumerable<TIn> inputEnumerable,
        Func<TIn, Task<TOut>> asyncProcessor,
        int? maxDegreeOfParallelism = null)
    {
        IEnumerable<Task<TOut>> tasks;

        if (maxDegreeOfParallelism != null)
        {
            SemaphoreSlim throttler = new SemaphoreSlim(maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value);

            tasks = inputEnumerable.Select(
                async input =>
                    {
                        await throttler.WaitAsync();
                        try
                        {
                            return await asyncProcessor(input).ConfigureAwait(false);
                        }
                        finally
                        {
                            throttler.Release();
                        }
                    });
        }
        else
        {
            tasks = inputEnumerable.Select(asyncProcessor);
        }

        await Task.WhenAll(tasks);
    }

0

这样做没有帮助,GetData是一个异步方法,因此并行调用它没有任何意义。 - svick

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