以下代码是异步检索来自多个网址的内容,并且一旦通过Task.WhenAny下载了一个内容,就会对其进行处理。但在处理过的部分,我需要Identifier对象。我认为向您展示代码更清楚:
var downloadTasks = new List<Task<string>>();
foreach (var identifier in input.Identifiers)
{
string url = BuildUrl(identifier, input.PeriodInYear, input.Interval);
var data = _webRequest.GetData(url, token);
downloadTasks.Add(data); // Here I only add the data, but not the Identifier. I thought about using a List<Tuple<Identifier, Task<string>>, but then I can't use it with Task.WhenAny(...)
}
while (downloadTasks.Count > 0)
{
var finishedDownloadTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedDownloadTask);
foreach (var content in await finishedDownloadTask)
{
// hereI I also need the Identifier object here !
}
}
这里是GetData的代码:
public virtual async Task<string> GetData(string uri, CancellationToken token)
{
// log removed
// try catch removed
string result = string.Empty;
using (var client = new HttpClient())
using (var response = await client.GetAsync(uri, token).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
else
logger.Error("Unable to retrieve data from the following url: {0} - StatusCode: {1}", uri, response.StatusCode);
}
return result;
}
答案 0 :(得分:7)
我不认为“构建任务列表”,“await Task.WhenAny
”,“从列表中删除已完成的任务”方法非常干净。
当我退后一步,查看代码并编写一个执行“初始”处理以及“后处理”的新异步方法时,我发现我的代码通常更干净。在您的示例中,它看起来像这样:
async Task GetDataAndPostProcessAsync(Identifier identifier, CancellationToken token)
{
var url = BuildUrl(identifier, input.PeriodInYear, input.Interval);
var content = await _webRequest.GetDataAsync(url, token);
// Use 'content' with 'identifier'
}
...
var tasks = input.Identifiers.Select(identifier =>
GetDataAndPostProcessAsync(identifier, token)).ToList();
await Task.WhenAll(tasks);
我在C#Cookbook的“我的并发”的配方2.6中详细介绍了这一点。