如何运行多个任务,处理异常并仍然返回结果

Mic*_*l K 14 c# task-parallel-library

我正在更新我的并发技能.我的问题似乎相当普遍:从多个Uris读取,解析并使用结果等.我在C#Cookbook中使用了Concurrency.有一些使用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);
}
Run Code Online (Sandbox Code Playgroud)

我需要的是用于运行多个异步任务的异步模式,捕获全部或部分成功.

  1. 网址1成功
  2. 网址2成功
  3. Url 3失败(超时,坏Uri格式,401等)
  4. 网址4成功
  5. ... 20多个成功的混合

如果任何失败,等待DownloadAllAsync任务将抛出单个聚合异常,从而丢弃累积的结果.从我的有限研究来看,WhenAll或WaitAll表现相同.我想捕获异常,记录失败,但继续执行剩余的任务,即使它们都失败了.我可以一个接一个地处理它们,但这不会破坏允许TPL管理整个过程的目的吗?是否有一个链接到一个模式,以纯TPL方式实现这一点?也许我使用的是错误的工具?

Ste*_*ary 20

我想捕获异常,记录失败,但继续执行剩余的任务,即使它们都失败了.

在这种情况下,最干净的解决方案是更改代码对每个元素所做的操作.即,这个当前的代码:

var downloads = urls.Select(url => httpClient.GetStringAsync(url));
Run Code Online (Sandbox Code Playgroud)

说"为每个网址,下载一个字符串".你想要它说的是"为每个网址,下载一个字符串,然后记录并忽略任何错误":

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
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 是的。技术评论家也提出了这个问题。:) 我保留了 `static` 方法,因为它们巧妙地强调了 `async` 方法最好在没有状态的情况下工作的概念。 (2认同)