ANK*_*ANK 1 .net c# exception .net-core worker-service
我的 Program.cs 中的 .net core 3.1 有一个工作人员服务,我有以下代码
public static void Main(string[] args)
{
try
{
CreateHostBuilder(args).Build().Run();
}
catch(Exception ex)
{
Handler(ex);
}
}
Run Code Online (Sandbox Code Playgroud)
还有一个工人阶级如下
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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果 Task.Delay 首先执行,然后第二次迭代的 GetData() 方法中发生任何异常,则 Main 方法中的 try catch 不会捕获该异常。我怎样才能在我的主要方法中捕获这些异常?
这是预期的行为。
文件中的 catch 块Program.cs将仅处理构建和启动 .NET core 通用主机时引发的异常。就这样。catch 块不会处理注册的托管服务在执行期间抛出的异常。
如果您检查基类的源代码,BackgroundService您就会明白为什么后台服务抛出的异常会丢失。
在这一行,您会看到调用了您的实现ExecuteAsync,并且生成的任务存储在_executeTask字段内。
您的重写ExecuteAsync是一个async函数。当函数内部存在未处理的异常时async,运行时会捕获该异常并将其放置在返回的Task. Task当等待或调用类似 api时,将抛出异常(保留其原始堆栈跟踪)Task.Wait()。
请注意,_executeTask不会等待任务,并且执行线程不会尝试通过调用该Task.Wait()方法来同步等待任务完成。因此,没有机会观察由此产生的异常Task。
只有一种可能观察到来自您的实现的异常ExecuteAsync。如果在第一条指令之前发生异常,则方法执行将同步完成,并且返回的 Task 属性将设置为 true ,其属性将为。在这种情况下,任务将返回到调用代码(参见这一行)。awaitIsCompletedStatusFaulted_executeTask
调用代码基本上是启动 .NET Core 通用主机的代码,它将返回错误的任务,并通过等待任务本身来检测启动后台服务时发生的错误。此时,来自该ExecuteAsync方法的异常将被重新抛出,并且 Program.cs 中的 catch 块将处理该异常。
ExecuteAsync 如果在第一个等待指令之后发生任何异常,.NET Core 通用主机将无法观察异常本身。这些异常将导致静默失败。您可以通过阅读这篇精彩的博客文章找到更多详细信息。
正如对您的问题的评论所指出的,这种行为在 .NET 6 中已更改,您可以在此处阅读。简而言之,.NET 6 中的默认行为是记录来自后台服务的任何未处理的异常并停止主机,以便您意识到问题,并且有机会修复代码并调查它抛出的原因例外情况。