无法在 try catch 中获取异常

Unable to get the exceptions in try catch

我的 .net core 3.1 中有一个工人服务 Program.cs 我有以下代码

public static void Main(string[] args)
{
    try
    {
        CreateHostBuilder(args).Build().Run();
    }
    catch(Exception ex)
    {
        Handler(ex);
    }
}

还有工人class如下

public class Worker : BackgroundService
{ 
  protected override async Task ExecuteAsync(CancellationToken Token)
  {
    
    do
    { 
     if(condition)
     {
      await _test.GetData();
     }
     await Task.Delay(500,Token);
    }
    while (!Token.IsCancellationRequested);

  }
 }

如果 Task.Delay 先执行,然后在第二次迭代的 GetData() 方法中发生任何异常,则它不会被我的 Main 方法中的 try catch 捕获。我怎样才能让这些异常在我的主要方法中被捕获?

这是预期的行为。

Program.cs 文件中的 catch 块将 处理在构建和启动 .NET 核心通用主机时抛出的异常.就这样。 catch 块不应处理已注册的托管服务在执行期间抛出的异常。

如果您查看 BackgroundService 基础 class 的源代码,您就会明白为什么后台服务抛出的异常会丢失。

在此 line 您会看到 ExecuteAsync 的实现被调用并且 结果任务存储在 _executeTask 字段中。

您对 ExecuteAsync 的覆盖是一个 async 函数。当 async 函数内部存在未处理的异常时,运行时会捕获异常并将其放置在返回的 Task 中。当等待 Task 或调用 Task.Wait() 之类的 api 时,将抛出异常(保留其原始堆栈跟踪)。
请注意,未等待 _executeTask 任务,并且执行线程不会尝试通过调用 Task.Wait() 方法来同步等待任务完成。因此,没有机会观察来自 Task.

的异常

只有一种可能会观察到来自您实施 ExecuteAsync 的异常。如果异常发生在 before very first await 指令,则方法执行同步完成并且返回的 Task 将具有 IsCompleted 属性 设置为真,其 Status 将是 Faulted。在这种情况下,_executeTask 任务将返回给调用代码(请参阅此 line)。

调用代码,基本上是启动 .NET Core 通用主机的代码,将返回一个错误的任务,并通过等待任务本身来检测启动后台服务时发生的错误。届时,来自 ExecuteAsync 方法的异常将被重新抛出,您的 Program.cs 中的 catch 块将处理该异常。

如果在第一个 await 指令 ExecuteAsync 之后发生任何异常,.NET 核心通用主机无法观察异常本身。这些异常将导致静默失败。您可以通过阅读 this great blog post.

找到更多详细信息

正如对您的问题的评论所指出的,此行为已在 .NET 6 中更改,您可以阅读 here。简而言之,.NET 6 中的默认行为是记录来自后台服务的任何未处理的异常并停止主机,以便您了解问题并有机会修复您的代码并调查它抛出的原因例外。