等待任务.当所有(任务)异常处理时,记录任务中的所有异常



我正试图从下面的代码中找出如何报告任务列表引发的所有异常。

这个代码片段的基本思想是:用户向处理程序发送一个请求,处理程序创建消息tasks并将它们发送到一个类,该类将它们发送给外部系统。我包括了以下涉及的方法。

我必须关闭一些东西,因为我正在调试异常处理程序,而任务exception总是为null,因为除非我在断点中停留足够长的时间,否则它们的状态似乎是Waiting for Activiation

// Handle the user request
public async void Handle(WriteScanToSys settings)
{
  _successfulScanIds = new List<int>();
  // create the messages as tasks
  var tasks = _factory.CreateMessage(settings).Select(msg => SendScans(msg));
  try
  {
    // wait for all of them to complete
    await Task.WhenAll(tasks); // I had ConfigureAwait(false) here, but took it off
  }
  catch (Exception)
  {
    foreach (var task in tasks.Where(t => t.Exception != null))
    {
      // ELMAH
      var errorLog = ErrorLog.GetDefault(null);
      errorLog.Log(new Error(task.Exception));
    }
  }
  // save to repository
}
// the task to perform
private async Task<IDictionary<string, object>> SendScans(IDictionary<string, object> data)
{
  object sysMsg = null;
  var response = await _msgCenter.SendMessage(data);
  response.TryGetValue("SystemMessage", out sysMsg);
  _successfulScanIds.Add(Convert.ToInt32(data["Id"]));
  return response;
}
// the communication with the external system (The message center class)
private async Task<IDictionary<string, object>> SendMessage(IDictionary<string, object> msg)
{
  var response = new Dictionary<string, object>();
  var response = await _client.sendAsync(
                          new BodyOfRequest(
                              // Compose Object
                          ));
  if (response.ScreenMessage != "SUCCESSFUL")
    throw new CustomException("The transaction for job " + job + " failed with msg: " + body.ScreenMessage);
  response.Add("SystemMessage", body.ScreenMessage);
  return response;
}

您违反了懒惰评估-每次迭代Select的结果都会创建一组新的任务。只需调用ToList():即可解决此问题

var tasks = _factory.CreateMessage(settings)
                    .Select(msg => SendScans(msg))
                    .ToList();

这样,您正在等待的任务集将与foreach循环检查的任务集相同。

您可以从Task.WhenAll-Task:中获得异常(如果有的话),而不是迭代所有任务

var taskResult = Task.WhenAll(tasks);
try
{
    await taskResult;
}
catch (Exception e)
{
    if (taskResult.IsCanceled)
    {
        // Cancellation is most likely due to a shared cancellation token. Handle as needed, possibly check if ((TaskCanceledException)e).CancellationToken == token etc.       
    }
    else if (taskResult.IsFaulted)
    {
        // use taskResult.Exception which is an AggregateException - which you can iterate over (it's a tree! .Flatten() might help)
        // caught exception is only the first observed exception
    }
    else
    {
        // Well, this should not really happen because it would mean: Exception thrown, not faulted nor cancelled but completed
    }
}

最新更新