0

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

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

还有一个工人阶级如下

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 捕获。我怎样才能让这些异常被我的主要方法捕获?

4

1 回答 1

2

这是预期的行为。

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

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

在这一,您会看到ExecuteAsync调用了您的实现,并且生成的任务存储在该_executeTask字段中。

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

只有一种可能性可以观察到来自您的ExecuteAsync. 如果在第一条指令之前发生异常,则方法执行同步完成,返回的 Task 的属性将设置为 true ,其值为。在这种情况下,任务将返回给调用代码(参见这一)。 awaitIsCompletedStatusFaulted_executeTask

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

如果ExecuteAsync 第一个 await 指令之后发生任何异常,则 .NET 核心通用主机无法观察到异常本身。这些异常将导致静默失败。您可以通过阅读这篇精彩的博客文章找到更多详细信息。

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

于 2022-02-10T22:29:52.183 回答