使用异步线程的退出代码退出应用程序

Posted

技术标签:

【中文标题】使用异步线程的退出代码退出应用程序【英文标题】:Exit the application with exit code from async thread 【发布时间】:2021-09-24 00:02:09 【问题描述】:

我的 .NET5 Web API 项目(除了 REST)监听 Telegram 消息。目标很简单:

当用户发送 exit 消息时 - 使用退出代码 8 终止应用 当消息处理程序抛出 SeriousException - 使用退出代码 8 终止应用程序

不幸的是,由于消息轮询的异步性质,我正在努力实现这一目标。我在 GitHub 中准备了一个示例项目:https://github.com/chris-rutkowski/AsExPoC。

我在 Stack Overflow 上发现了许多类似的主题,但没有什么真正适合我,而且大量建议的解决方案(一些 AsyncHelpers 等)似乎有点矫枉过正。我正在考虑将Thread.CurrentThread 传递给服务并以某种方式抛出异常,但我不知道如何,或者它是否可能发生,即使它是最佳实践。我对 C#+.NET 了解不多,但是在 Swift (ios) 中,我会很容易地解决它,只需 3 行,包括大括号:

  DispatchQueue.main.async 
      throw ForcedExitException()
  

这是我精简的 .NET5 Web Api,您可以在 repo 中找到。

Program.cs

public class Program
    
        public static int Main(string[] args)
        
            try
            
                var host = CreateHostBuilder(args).Build();
                var service = host.Services.GetRequiredService<DummyService>();
                service.Run();

                // other services are running too - I cannot just wait for DummyService to throw the exception

                host.Run();

                return 0;
            
            catch (ForcedExitException)
            
                return 8;
            
            catch (Exception ex)
            
                return 1;
            
        

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                
                    webBuilder.UseStartup<Startup>();
                );
    

DummyService.cs

    public class DummyService
    
        private readonly DummyMessenger _dummyMessenger;

        public DummyService()
        
            _dummyMessenger = new DummyMessenger();
        

        public async void Run()
        
            _dummyMessenger.StartReceiving();
            _dummyMessenger.Add(async (_, message) => 
                Console.WriteLine($"received message message");
                await Task.Delay(50);

                if (message == "exit") 
                    throw new ForcedExitException();
            );
        
    

我的期望:

Exited with error code 8

这是我在控制台中看到的:

Exited with error code 134, with the following stacktrace
Unhandled exception. AsExPoC.ForcedExitException: Exception of type 'AsExPoC.ForcedExitException' was thrown.
at AsExPoC.DummyService.<>c.<Run>b__2_0(Object _, String message) in /Users/chris/Developer/Projects/AsExPoC/AsExPoC/DummyService.cs:line 36
at AsExPoC.DummyMessenger.StartReceiving() in /Users/chris/Developer/Projects/AsExPoC/AsExPoC/DummyMessenger.cs:line 27
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__140_1(Object state)
at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() 

感谢反馈。

【问题讨论】:

我没有考虑太多,但你试过Environment.Exit(Int32) @TheGeneral 感谢您的关注。它不会改变任何东西。我在 GitHub 上创建了示例项目:github.com/chris-rutkowski/AsExPoC。同时,我将更新我的问题以使其更简单。 【参考方案1】:

我将首先将您长期运行的服务移至IHostedService,如果服务停止,则强制主机停止;

public class DummyService : BackgroundService

    private readonly IHostApplicationLifetime lifetime;

    public DummyService(IHostApplicationLifetime lifetime)
    
        this.lifetime = lifetime;
    

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    
        try
        
            while (true)
            
                //...
            
        
        catch (Exception)
        
            Environment.ExitCode = 8;
        
        lifetime.StopApplication();
    

然后你可以从你的 main 方法中返回Environment.ExitCode,或者将它保留为void return。

【讨论】:

以上是关于使用异步线程的退出代码退出应用程序的主要内容,如果未能解决你的问题,请参考以下文章

shell 异步任务模拟多线程

线程以代码 0 (0x0) 退出,没有未处理的异常

大厂面试必备:docker退出容器之后会不会保存

VS2013,C++:未知的“线程 X 以代码 0 退出”

从线程捕获异常时退出程序

Aliyun OSS SDK 异步分块上传导致应用异常退出