我正在构建一个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);
}
当我看到提琴手时,请求全部都是连续的;它需要几秒钟来执行和处理所有这些。但是,我不希望代码等到1个请求完成后才转移到下一个请求,我想同时执行多个请求。
通常我会使用Parallel.Invoke()
或Parallel.ForEach()
或类似的东西来执行此操作,但显然并行库在Windows Phone 8中不可用。
那么实现这一目标的最佳方法是什么? Task.Factory.StartNew()
? new Thread()
?
答案 0 :(得分:10)
无需在单独的线程上运行每个请求。您可以轻松地执行此操作:
var raw = await Task.WhenAll(uris.Select(uri => httpClient.GetStringAsync(uri)));
var processed = raw.Select(data => dataProcessor.Process(data)).ToArray();
此代码获取uris
的集合,并为每个代码(Select(...)
)启动HTTP下载。然后它异步地等待它们全部完成(WhenAll
)。然后在第二行代码中处理所有数据。
但是,Windows Phone运行时可能会限制您对同一服务器的最大请求数。
答案 1 :(得分: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个请求,除非他们是不同的服务器。在同一台服务器上同时向Uris发出30个请求不太可能改善您的处理时间。 (幸运的是,TPL可能会为您解决这个问题,因为它会稍微限制并行性。)