【发布时间】:2020-07-21 04:08:51
【问题描述】:
我正在使用 TPL 和 async/await 在 webclient 之上为我的应用程序构建异步 API。很少有地方(通常是我需要运行一堆异步任务并最终等待所有这些任务的地方),我遵循代码 sn-p。我只是想确保我得到它是正确的,即使使用 TPL 和 async/await 编写异步代码相对容易 调试/故障排除仍然具有挑战性(交互式调试和客户站点的故障排除问题) - 所以想把它做好。
我的目标: 能够捕获原始任务、后续任务以及子任务产生的异常,以便我处理(如果需要)。我不希望任何例外都被遗忘。
我使用的基本原则: 1. .net 框架确保将异常附加到任务 2. try/catch 块可以应用于 async/await 以提供同步代码的错觉/可读性(参考:http://channel9.msdn.com/Events/TechDays/Techdays-2014-the-Netherlands/Async-programming-deep-dive、http://blogs.msdn.com/b/ericlippert/archive/2010/11/19/asynchrony-in-c-5-part-seven-exceptions.aspx、http://msdn.microsoft.com/en-us/library/dd537614.aspx 等)
问题:我希望获得批准,即我希望的目标(我可以从原始任务、延续任务和子任务中捕获异常)已经实现,并且我可以对示例:
例如,是否会出现这样一种情况,其中一个组合任务(例如,解包的代理任务)根本不会被激活(waitforactivation 状态),因此 waitall 可能只是等待任务启动?我的理解是,这些情况永远不会发生,因为延续任务总是执行,并返回一个已由代理使用 wnwrap 跟踪的任务。只要我在所有层和 api 中遵循类似的模式,该模式就应该捕获链式任务中的所有聚合异常。
注意: 本质上是在寻找建议,例如,如果原始任务状态未完成,则避免在继续任务中创建虚拟任务,或者使用附加到父级以便我只能等待父级等. 查看所有可能性,以便我可以选择最佳选项,因为这种模式在我的应用程序中严重依赖于错误处理。
static void SyncAPIMethod(string[] args)
{
try
{
List<Task> composedTasks = new List<Task>();
//the underlying async method follow the same pattern
//either they chain the async tasks or, uses async/await
//wherever possible as its easy to read and write the code
var task = FooAsync();
composedTasks.Add(task);
var taskContinuation = task.ContinueWith(t =>
{
//Intentionally not using TaskContinuationOptions, so that the
//continuation task always runs - so that i can capture exception
//in case something is wrong in the continuation
List<Task> childTasks = new List<Task>();
if (t.Status == TaskStatus.RanToCompletion)
{
for (int i = 1; i <= 5; i++)
{
var childTask = FooAsync();
childTasks.Add(childTask);
}
}
//in case of faulted, it just returns dummy task whose status is set to
//'RanToCompletion'
Task wa = Task.WhenAll(childTasks);
return wa;
});
composedTasks.Add(taskContinuation);
//the unwrapped task should capture the 'aggregated' exception from childtasks
var unwrappedProxyTask = taskContinuation.Unwrap();
composedTasks.Add(unwrappedProxyTask);
//waiting on all tasks, so the exception will be thrown if any of the tasks fail
Task.WaitAll(composedTasks.ToArray());
}
catch (AggregateException ag)
{
foreach(Exception ex in ag.Flatten().InnerExceptions)
{
Console.WriteLine(ex);
//handle it
}
}
}
【问题讨论】:
-
这个问题似乎跑题了,因为它是一个代码审查问题,应该在 codereview.stackexchange.com 上
-
@Eugene,我知道我最有可能使用的模式 - 但目的是确保并使其变得更好。例如,在连续块中,我总是用任务创建虚拟任务,以确保我可以捕获未包装的任务错误。使用 unwrap 对嵌套任务也是如此。只是想确保使用正确的扩展(例如,将延续和子任务附加到原始任务并等待它会更好吗?)
-
IMO,使用
async/await,这段代码本可以更简单、更优雅。我不明白您为什么坚持使用ContinueWith和Unwrap,以及为什么将内部和外部(未包装)任务都添加到composedTasks。 -
@Noseratio,无论我在哪里使用/能够在异步 API/方法中使用异步/等待,我都只是将它们包装在 try/catch 中(是的,它更好更优雅)。但请注意,这不是“异步”方法,而是“同步”调用——有些 api 仍然需要 syn api 支持。因此,为此我不得不使用 continue/unwrap 来组合所有任务并最终等待它们。
-
@Noseration,现在我明白了:)。不过我有一个问题,请看下文。问候。
标签: c# .net asynchronous task-parallel-library async-await