执行多个任务时,为什么TaskCanceledException不存储在Task.Exception属性中?

时间:2016-10-07 12:49:35

标签: c# async-await task-parallel-library

我有一个应用程序,它为每个不同的项目执行RESTful API的几个后续HTTP请求。

我必须处理执行这些请求的异常的代码类似于Concurrency in C# Cookbook Stephen Cleary中描述的代码,方法2.4:

var tasks = new List<Task>();
foreach(var item in items)
{
    tasks.Add(ProcessItemAsync(item));
}
var parentTask = Task.WhenAll(tasks);
try {
    await parentTask;
} catch {
    var exceptions = parentTask.Exception;
    if (exceptions != null) {
        // log individual exceptions
    }
}

在分析日志时,我注意到某些处理没有导致发送所有需要的请求。但是,日志中没有记录任何例外情况。

调试应用程序时,我发现调用其中一个端点导致由于Web API超时而抛出TaskCanceledException。但是,parentTask.Exception属性为null,因此,我没有在日志中正确记录此异常。

我的问题如下:

  • 为什么TaskCanceledException不存储在Task.Exception属性中?
  • 是否有任何其他异常不会存储在类似于TaskCanceledException的Task.Exception属性中?

3 个答案:

答案 0 :(得分:5)

您可以通过阅读有关任务取消的this documentation来获取有关此内容的更多信息,尤其是此部分:

  

如果您正在等待转换到已取消状态的任务,a   System.Threading.Tasks.TaskCanceledException异常(包装在   抛出AggregateException异常)。注意这个例外   表示取消成功而不是错误的情况。   因此,任务的异常属性返回null

所以重点是 - 任务取消是一个预期的事情,它不是一个错误。因此没有理由设置Exception属性 - 有关取消的信息已记录在Task状态。

另一个故事是超时 是一个错误,不应导致任务取消。另一方面,待处理的网页请求 在一段时间后(超时后)被取消,因此可以辩解。

作为一种变通方法,您可以检查tasks数组中的所有任务,看看它们是否处于取消状态(IsCancelled返回true)并相应地记录。

答案 1 :(得分:2)

TaskCanceledException由Task专门处理。

在Task中未处理TaskCanceledException时。它只是将任务中的IsCanceled属性设置为true。

注意,如果等待Task将抛出TaskCanceledException异常,而Task.Exception将保持为null。

答案 2 :(得分:2)

虽然可能处理汇总异常,但我认为 clean 可以单独处理它们:

var tasks = new List<Task>();
foreach(var item in items)
  tasks.Add(ProcessItemAndLogExceptionsAsync(item));
await Task.WhenAll(tasks);

private static async Task ProcessItemAndLogExceptionsAsync(Item item)
{
  try
  {
    await ProcessItemAsync(item);
  }
  catch (Exception ex)
  {
    // Log exception
  }
}

通过这种方式,您也可以获得OperationCanceledException