【问题标题】:System.AggregateException in await Task.WhenAll()等待 Task.WhenAll() 中的 System.AggregateException
【发布时间】:2018-05-11 18:32:07
【问题描述】:

我有一个从数据库中检索行列表的进程,并且每行必须执行一个长时间运行的操作列表:

  1. 将文件上传到 FTP 服务器
  2. 每 1 分钟检查一次特定文件(基于 1 中上传的文件创建)在此类 FTP 服务器中是否可用
  3. 下载第 2 步中找到的文件(一旦可用)
  4. 将下载的文件内容保存到数据库中

我已经实现了一个方法,它按照上述顺序执行所有这些步骤:我想并行(异步)多次调用该方法:调用它的次数将取决于检索到的行数工作流程的开始。

主要方法的提取如下:

List<Model> ftpModels = _service.CreateFtpModel(rowsFromDB);
List<Task> asyncTasks = new List<Task>();

ftpModels.AsParallel().ForAll(p =>
{
    asyncTasks.Add(DoStepsAsync(p));
});
// Wait for all the tasks to finish.
await Task.WhenAll(asyncTasks.Where(p => p != null));

首先,根据从数据库中检索到的行创建模型列表, 然后创建一个空的任务列表,然后遍历创建的模型以执行异步方法“DoStepsAsync”,该方法检索要插入到 asyncTasks 列表中的任务。最后,它为所有任务调用 Tasks.WellAll。

这种方法通常工作正常,但我碰巧发现有时(随机与线程相关的问题)在 WhenAll 方法上抛出下一个异常:

System.AggregateException:出现一个或多个错误。 ---> System.ArgumentException:任务参数包含一个空值。

这清楚地表明,上面的任何任务在执行时都返回了 null(而不是任务)。然后我阅读了我正在写的日志,但我在任何地方都没有发现任何异常,只是流程突然中断,落入 System.AggregateException

我在谷歌上搜索和阅读了很多,这个问题发生在:

至少有一个任务实例被取消。如果任务被取消,则 AggregateException 异常在其 AggregateException.InnerExceptions 集合中包含 OperationCanceledException 异常。 -要么- 在至少一个 Task 实例的执行过程中引发了异常。

但是没有抛出异常,因为我已经相应地实现了 try-catch 块。因此,我不知道到底发生了什么。

有什么建议吗?

只是为了提供更多关于 DoStepsAsync() 方法中发生的事情的信息,对 FTP 客户端(线程安全)进行异步调用,写入日志和类似的东西。真正让我吃惊的是,即使上面提到的所有 catch 块,任何地方都没有捕获到异常。

任何建议都非常感谢。

【问题讨论】:

  • exception is thrownexception is thrown时你有没有尝试让调试器中断?
  • 是的,但在调试模式下不会发生。仅在部署应用程序的服务器中,正如我所说,并非总是如此。
  • asyncTasks.Where(p =&gt; p != null)) 与上面的p 不同,它是Task,它可能永远不会为空。您的意思是在 ForEach 之前过滤空的 p 吗?
  • 附带说明,如果您使用Select 来生成asyncTasks 集合而不是变异的ForEach,则语法会更简洁,因为在ForEach 中运行的唯一命令是List&lt;T&gt;.Add
  • 异常来自具有“tasks”参数的方法。似乎您的代码 sn-p 不能产生该异常。试着在你的程序中寻找另一个地方,在那里你可以调用 WhenAll、WhenAny 等。不幸的是,异步代码没有提供好的调用堆栈来识别特定的调用。

标签: c# async-await task


【解决方案1】:

发现问题:一旦我通过 AsParallel().ForAll 遍历模型列表,可能会发生(取决于 CPU 工作负载)要创建的任务(异步)尚未创建但正在被WhenAll 方法(也是异步的),因此会抛出上面提到的异常。正如我所说,根据 CPU 工作负载,有时会发生这种情况:为了轻松重现问题,我创建了一个单元测试,它最多调用有问题的方法 50 次,并且通常在前 10 次运行中失败。 为了解决这个问题,我已经用ForEach 替换了无用的(因为在这种情况下不会带来任何相关的)函数AsParallel().ForAll 并且问题不再发生,因为 tasks 总是按照顺序创建(但不执行,它们是异步执行的),无论 CPU 工作负载如何。代码如下,解决问题:

List<Model> ftpModels = _service.CreateFtpModel(rowsFromDB);
List<Task> asyncTasks = new List<Task>();

ftpModels.ForEach(p =>
{
    asyncTasks.Add(DoStepsAsync(p));
});
// Wait for all the tasks to finish.
await Task.WhenAll(asyncTasks.Where(p => p != null));

我希望它在未来对其他人有所帮助。

【讨论】:

    猜你喜欢
    • 2023-03-26
    • 2016-08-22
    • 1970-01-01
    • 2016-01-30
    • 2012-11-06
    • 2021-07-17
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多