我正在更新我的并发技能。我的问题似乎相当普遍:从多个Uris读取,解析并使用结果等等。我有Concurrency in C# Cookbook。有一些使用GetStringAsync的例子,例如
static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
var httpClient = new HttpClient();
var downloads = urls.Select(url => httpClient.GetStringAsync(url));
Task<string>[] downloadTasks = downloads.ToArray();
string[] htmlPages = await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
我需要的是运行多个异步任务的异步模式,捕获全部或部分成功。
等待DownloadAllAsync任务将抛出单个聚合异常,如果有任何失败,则丢弃累积的结果。从我的有限研究来看,WhenAll或WaitAll表现相同。我想捕获异常,记录失败,但继续执行剩余的任务,即使它们都失败了。 我可以一个一个地处理它们,但是这不会破坏允许TPL管理整个过程的目的吗?是否有一个链接到一个模式,以纯TPL方式实现这一点?也许我使用了错误的工具?
答案 0 :(得分:19)
我想捕获异常,记录失败,但继续执行剩余的任务,即使它们都失败了。
在这种情况下,最干净的解决方案是更改代码为每个元素执行的操作。即,这个当前代码:
var downloads = urls.Select(url => httpClient.GetStringAsync(url));
对每个网址说&#34;下载一个字符串&#34;。你想要它说的是&#34;对于每个网址,下载一个字符串,然后记录并忽略任何错误&#34;:
static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
var httpClient = new HttpClient();
var downloads = urls.Select(url => TryDownloadAsync(httpClient, url));
Task<string>[] downloadTasks = downloads.ToArray();
string[] htmlPages = await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
static async Task<string> TryDownloadAsync(HttpClient client, string url)
{
try
{
return await client.GetStringAsync(url);
}
catch (Exception ex)
{
Log(ex);
return string.Empty; // or whatever you prefer
}
}
答案 1 :(得分:4)
您可以为所有任务附加延续并等待它们,而不是直接等待任务。
static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
var httpClient = new HttpClient();
IEnumerable<Task<Task<string>>> downloads = urls.Select(url => httpClient.GetStringAsync(url).ContinueWith(p=> p, TaskContinuationOptions.ExecuteSynchronously));
Task<Task<string>>[] downloadTasks = downloads.ToArray();
Task<string>[] compleTasks = await Task.WhenAll(downloadTasks);
foreach (var task in compleTasks)
{
if (task.IsFaulted)//Or task.IsCanceled
{
//Handle it
}
}
var htmlPages = compleTasks.Where(x => x.Status == TaskStatus.RanToCompletion)
.Select(x => x.Result);
return string.Concat(htmlPages);
}
只要一个任务失败,它就不会停止,而是等待所有任务完成。然后分别处理成功和失败。