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

Jea*_*ler 4 c# compiler-optimization sigterm .net-core .net-6.0

我正在尝试在 Linux Arm 设备上正常关闭我的控制台应用程序。发送的信号是SIGTERM我使用新的PosixSignalRegistration.Create()方法实现了 Posix Sgnal Handler 。当在调试模式下编译时,这可以完美地工作。但是,在发布模式(打开优化)下,永远不会进入处理程序。

这是一个 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");
}
Run Code Online (Sandbox Code Playgroud)

在调试模式下,调用 时pkill my-application -15,日志如下所示,并且应用程序关闭:

Received SIGTERM
finally
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
Application shut down
Run Code Online (Sandbox Code Playgroud)

在发布模式下,唯一发生的事情是以下日志消息:

info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
Run Code Online (Sandbox Code Playgroud)

Ctrl + C然而,应用程序继续运行,并且只能通过或正常关闭pkill my-application -2

我缺少什么?

Evk*_*Evk 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");
    }
}
Run Code Online (Sandbox Code Playgroud)