如何在Windows Phone上运行并行任务?

5
我正在开发一个WP8应用程序,需要执行约30个Web请求。这些请求彼此独立,因此可以并行处理。
我的代码如下(简化/伪代码):
foreach (Uri uri in uris)
{
    var rawData = await Task.Run(() => httpClient.GetStringAsync(uri).ConfigureAwait(false));

    var processedData = dataProcessor.Process(rawData);
    processedDataCollection.Add(processedData);
}

当我查看Fiddler时,请求都是按顺序执行的;执行和处理所有请求需要几秒钟时间。然而,我不希望代码在移动到下一个请求之前等待1个请求完成,我想同时执行多个请求。
通常我会使用Parallel.Invoke()或Parallel.ForEach()之类的方法来做到这一点,但显然Parallel库在Windows Phone 8中不可用。
那么实现这一点的最佳方法是什么?Task.Factory.StartNew()?new Thread()?
2个回答

10

没必要为每个请求开启一个单独的线程,你同样可以采用以下方式:

var raw = await Task.WhenAll(uris.Select(uri => httpClient.GetStringAsync(uri)));
var processed = raw.Select(data => dataProcessor.Process(data)).ToArray();

这段代码获取 uris 集合,并为每个 URI 开始 HTTP 下载(Select(...))。然后它异步等待它们全部完成 (WhenAll)。所有数据在第二行代码中处理。

但是,很可能 Windows Phone 运行时会限制您对同一服务器的最大请求数。


我真的很喜欢这个。非常紧凑和优雅。我之前无法让它与其他代码配合工作,但这个完美地解决了问题。如果您能解释一下步骤以及为什么它实际上有效,那么您的帖子将更有帮助。 - Leon Cullens
1
更新以包含更多的解释。 - Stephen Cleary

3
您正在使用 await 与您创建的任务,这意味着在继续方法之前,您正在等待调用完成。正如您所指出的那样,您的 foreach 没有并行化,因此您实际上正在进行多个串行请求。
您可以使用 Task.WaitAll 方法使其按照您的意图运行(可能)。像这样的东西应该足够:
var tasks = new List<Task>();

foreach (Uri uri in uris)
{
    var thisTask = Task.Run(() => 
                       { 
                         httpClient.GetStringAsync(uri).ConfigureAwait(false);
                         var processedData = dataProcessor.Process(rawData);
                         processedDataCollection.Add(processedData);
                       });

    tasks.Add(thisTask);
}

Task.WaitAll(tasks);

请注意,这也将并行处理您的数据(也就是说,它也会并行调用.Process),而原始代码没有这么做。
我还要补充一点,如果这30个请求都是发送到同一个服务器上,那你可能不想同时运行这30个请求,因为这不太可能提高处理时间。(幸运的是,TPL 可能会帮你限制并行性。)

在你的例子中,GetStringAsync()返回什么?我只能让它返回一个Task<T>ConfiguredTaskAwaitable<T>。我该如何将其用于Process()方法?我需要在GetStringAsync方法上调用.Result吗? - Leon Cullens
使用await Task.WhenAll而不是Task.WaitAll不是更好吗?或者它们有不同的结果吗? - Leon Cullens
Task.WhenAll 创建一个新任务,直到所有其他任务完成后才会完成;Task.WaitAll 只是等待它们完成。如果您不关心它们何时完成并且可以继续进行,可以完全省略该调用。关于 GetStringAsync... - Dan Puzey
我所做的只是将您已经调用的代码包装起来,以展示如何使其并行运行。您没有对返回值进行任何操作,也没有真正指定您想要做什么,因此我没有更改该代码。GetStringAsync返回一个任务,因为它是一个异步方法。如果您想获取结果,可以使用string s = await httpClient.GetStringAsync(uri); - 我不确定您调用.ConfigureAwait(false)的意图是什么。 - Dan Puzey

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