【问题标题】:await Task.WhenAll(tasks) Exception Handling, log all exceptions from the tasksawait Task.WhenAll(tasks) 异常处理,记录来自任务的所有异常
【发布时间】:2015-05-03 02:25:54
【问题描述】:

我试图弄清楚如何从下面的代码中报告由任务列表引发的所有异常。

这段代码sn-p的基本思想是:用户向处理程序发送请求,处理程序创建消息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;
}

【问题讨论】:

    标签: c# asynchronous exception-handling async-await


    【解决方案1】:

    你已经犯了懒惰的评估 - Select 的结果将在你每次迭代它时创建一组新的任务。你可以通过调用ToList()来解决这个问题:

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

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

    【讨论】:

    • 你知道我想过这个问题,但当时因为一个我现在想不出的原因把它搞砸了。我应该知道这一点,尤其是当调试器在尝试钻取时无法评估任务时。有时我想它需要别人的眼睛。谢谢@JonSkeet
    【解决方案2】:

    您可以从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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多