异步任务中没有异常记录

No exception logged in async task

我发现了很多类似的帖子,但 none 概述了我遇到的具体问题。

我有一个 C# Worker 项目。在工作人员的 StartAsync 方法中,我调用 await TryConnect(); TryConnect 看起来像这样:

private async Task TryConnect()
{
        Logging.Log("1");
        var startResult = await StartUserStreamAsync();

        Logging.Log("2");
        if (!startResult.Success)
        {
            Logging.Log("3");
            throw new Exception($"Failed to start: {startResult.Error}");
        }
        listenKey = startResult.Data;
        Logging.Log("4");
}

调用站点如下所示:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
        //initialize
        await Initialize();            
        ///other code
}

private async Task Initialize()
{
        ///other code
        await TryConnect();
        ///other code
}

正如您可能猜到的那样。 1、2 和 3 已登录到控制台。 但是,4 不是,抛出异常。

在以前正常的完全同步控制台应用程序中。这只是抛出一个未捕获的异常,应用程序停止执行,一切都如我所料。我不打算从那种状态中恢复过来。 然而在异步领域,它保持沉默。没有抛出异常,除了调用方法的执行似乎停止之外,没有任何迹象表明有任何错误。

这是为什么,我应该做什么?我是否被迫 try/catch 它,如果是这样,如果你忘记了怎么办?你只是想弄清楚你不那么明显的错误吗?

您发布的代码的异步方面是正确的并且按预期工作。

但是,由于您使用的是 Worker,因此来自其他应用程序类型的假设不成立。

由于worker是通过BackgroundService实现的,ExecuteAsync方法只有在同步抛出时才会失败。这被解释为服务期间的错误 start-up。

一旦 ExecuteAsync 挂起(首先遇到执行实际异步工作的等待),BackgroundService 假定正常服务工作现在正在进行中并且已成功启动。之后,假设服务为运行.

一旦服务关闭(例如在服务管理器中停止,sudo systemctl stop 等),它将触发传递给 ExecuteAsync 的取消令牌并等待返回的 Task.此时,它会遇到你期望的异常。

我的理解是,服务不应该需要用户交互,它们应该继续做它们所做的事情,对过程中任何可能的错误保持弹性,向相应的 OS 设施(例如 EventLog)报告任何错误.因此,在 ExecuteAsync 中完成的任何代码都应该包装在适当的 try/catch 中,可能除了在关闭时从取消令牌中抛出的 OperationCanceledException

如果在某个时候,您意识到该服务无法再运行,您可以通过 IHostApplicationLifetime.StopApplication (doc)

终止托管进程

如果您在 start-up 期间需要异步工作,可以将您的 worker 直接实现为 IHostedService 并实现您认为合适的 StartAsync 方法。

另请参阅 BackgroundService source on GitHub for reference. Microsoft Docs also has Implement the IHostedService interface 教程。

我还可以建议阅读 .NET Generic Host,因为这是管理工作人员 BackgroundService 的底层基础设施。关于主机生命周期和相关概念的内容很多。

我有描述这两个 silent failure and application lifetime 问题的博文。总之,尽管这两种行为都令人惊讶,但它们都是设计使然。

在我自己的代码中,我使用派生自 BackgroundServiceCriticalBackgroundServiceBase 类型,并确保记录异常(而不是忽略),并确保在服务结束时应用程序结束。