无法在 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 中的默认行为是记录来自后台服务的任何未处理的异常并停止主机,以便您了解问题并有机会修复您的代码并调查它抛出的原因例外。
我的 .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 中的默认行为是记录来自后台服务的任何未处理的异常并停止主机,以便您了解问题并有机会修复您的代码并调查它抛出的原因例外。