合并任务会生成字典
本文关键字:字典 任务 合并 | 更新日期: 2023-09-27 18:36:50
我正在尝试并行化依赖外部资源的工作,并将其合并到一个生成的字典中。
为了说明我的需求,假设我想下载一组文件,并将每个结果放入字典中,其中键是 url:
string[] urls = { "http://msdn.microsoft.com", "http://www.stackoverflow.com", "http://www.google.com" };
var fileContentTask = GetUrls(urls);
fileContentTask.Wait();
Dictionary<string, string> result = fileContentTask.Result;
// Do something
但是,我能够编写GetUrls
方法。我可以生成所有任务,但我没有找到如何在字典中合并结果:
static Task<Dictionary<string,string>> GetUrls(string[] urls)
{
var subTasks = from url in urls
let wc = new WebClient()
select wc.DownloadStringTaskAsync(url);
return Task.WhenAll(subTasks); // Does not compile
}
如何将生成的任务合并到字典中?
您需要自己执行映射。例如,您可以使用:
static async Task<Dictionary<string,string>> GetUrls(string[] urls)
{
var tasks = urls.Select(async url =>
{
using (var client = new WebClient())
{
return new { url, content = await client.DownloadStringTaskAsync(url) };
};
}).ToList();
var results = await Task.WhenAll(tasks);
return results.ToDictionary(pair => pair.url, pair => pair.content);
}
请注意必须如何async
该方法,以便可以在其中使用await
。
利用现有 linq 的东西:
static async Task<Dictionary<string, string>> GetUrls(string[] urls)
{
IEnumerable<Task<string>> subTasks = from url in urls
let wc = new WebClient()
select wc.DownloadStringTaskAsync(url);
var urlsAndData = subTasks.Zip(urls, async (data, url) => new { url, data = await data });
return (await Task.WhenAll(urlsAndData)).ToDictionary(a => a.url, a => a.data);
}
但是由于这不会处理WebClient
,我会重构一种方法来使其如下所示。我还添加了一个Distinct
调用,因为下载两个相同的 url 只是在制作字典时会失败是没有意义的。
static async Task<Dictionary<string, string>> GetUrls(string[] urls)
{
var distinctUrls = urls
.Distinct().ToList();
var urlsAndData =
distinctUrls
.Select(DownloadStringAsync)
.Zip(distinctUrls, async (data, url) => new { url, data = await data });
return (await Task.WhenAll(urlsAndData)).ToDictionary(a => a.url, a => a.data);
}
private static async Task<string> DownloadStringAsync(string url)
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(url);
}
}