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

Exit the application with exit code from async thread

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

不幸的是,由于消息轮询的异步特性,我正在努力实现这个目标。我在 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() 

感谢反馈。

我首先将您的长 运行 服务移动到 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 方法 return Environment.ExitCode,或者将它保留为 void return.