在发布模式下,应用程序未进入Posix信号处理程序

4
我正在尝试在Linux ARM设备上优雅地关闭我的控制台应用程序。发送的信号是SIGTERM。我使用新的PosixSignalRegistration.Create()方法实现了一个Posix信号处理程序。当以调试模式编译时,这可以完美地工作。然而,在启用优化的发布模式下,处理程序从未被执行。
以下是一个最小可复现示例(MRE):
using Microsoft.Extensions.Hosting;
using System.Runtime.InteropServices;

ManualResetEvent resetEvent = new(false);
CancellationTokenSource cts = new();

// Handles CTRL + C in both operating systems
Console.CancelKeyPress += (sender, eventArgs) =>
{
    Console.WriteLine("Received CTRL+C");
    resetEvent.Set();
    eventArgs.Cancel = true;
    cts.Cancel();
};

// Handles graceful kill (Sigterm) in linux (e.g. systemctl stop)
PosixSignalRegistration.Create(PosixSignal.SIGTERM, (context) =>
{
    Console.WriteLine("Received SIGTERM");
    resetEvent.Set();
    cts.Cancel();
});

IHost? host = null;

try
{
    var hostBuilder = Host.CreateDefaultBuilder(args);
    host = hostBuilder.Build();

    host.Start();

    resetEvent.Reset();
    resetEvent.WaitOne();
}
catch
{
    Console.WriteLine("Error");
}
finally
{
    Console.WriteLine("finally");
    await (host?.StopAsync(TimeSpan.FromSeconds(5)) ?? Task.CompletedTask);
    Console.WriteLine("Application shut down");
}

在调试模式下,当调用pkill my-application -15时,日志如下,并且应用程序关闭:
Received SIGTERM
finally
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
Application shut down

在发布模式下,唯一发生的事情就是以下日志信息:
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...

然而,该应用程序仍在运行,只能通过Ctrl + Cpkill my-application -2优雅地关闭。

我错过了什么吗?

1个回答

5
请注意,PosixSignalRegistration.Create 方法并不返回 void,而是返回一个 PosixSignalRegistration 实例。该对象表示您处理程序的注册,它实现了 IDisposable 接口,调用 Dispose 方法将注销处理程序。它还有一个终结器,它执行与 dispose 相同的操作 - 注销处理程序。
现在请注意忽略 PosixSignalRegistration.Create 的返回值。现在上面提到的注册没有任何引用,垃圾回收器可以自由地收集它。由于它有一个终结器 - 当 GC 收集一个实例时,它将被调用并注销处理程序。
这就是在您的情况下发生的事情。在调试模式下,出于各种原因(如便于调试),GC 不会收集它。然而,在发布模式下,它可能会在调用 PosixSignalRegistration.Create 后立即被回收。
您可以在方法末尾添加 GC.KeepAlive(yourRegistration) 调用,但在这种情况下,这可能不是最好的选择,因为注册实现了 IDisposable 接口,所以您应该尊重它。
using (PosixSignalRegistration.Create(PosixSignal.SIGTERM, (context) => {
           Console.WriteLine("Received SIGTERM");
           resetEvent.Set();
           cts.Cancel();
       })) {

    IHost? host = null;

    try {
        var hostBuilder = Host.CreateDefaultBuilder(args);
        host = hostBuilder.Build();

        host.Start();

        resetEvent.Reset();
        resetEvent.WaitOne();
    }
    catch {
        Console.WriteLine("Error");
    }
    finally {
        Console.WriteLine("finally");
        await (host?.StopAsync(TimeSpan.FromSeconds(5)) ?? Task.CompletedTask);
        Console.WriteLine("Application shut down");
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接