我希望能够在c#控制台应用程序中捕获Ctrl+C,以便在退出之前执行一些清理。做这件事最好的方法是什么?


当前回答

检测SIGTERM和Ctrl+C:

CancellationTokenSource ctSource = new();
CancellationToken ct = ctSource.Token;

void ExitHandler()
{
    // You can add any arbitrary global clean up
    Console.WriteLine("Exiting...");
    ctSource.Cancel();
}

// Assign exit handler to be called when the process is terminated
// or the user hits CTRL+C
AppDomain.CurrentDomain.ProcessExit += (sender, args) => ExitHandler();
Console.CancelKeyPress += (sender, args) => ExitHandler();

// Then you can use the cancellation token to check for exit:
Console.WriteLine("Ready to gracefully shut down!");
while (!ct.IsCancellationRequested)
{
    Console.WriteLine($"Exit not detected, waiting another 10s.");
    Task.Delay(10000, ct).Wait(ct);
}

其他回答

看到MSDN:

控制台。CancelKeyPress事件

带有代码示例的文章:

Ctrl-C和.NET控制台应用程序

我可以在离开前做一些清理工作。做这件事最好的方法是什么 这才是真正的目标:走出陷阱,做你自己的东西。以上两个答案都不是正确的。因为Ctrl+C只是退出应用程序的众多方法之一。

在dotnet c#中需要什么-所谓的取消令牌传递给Host.RunAsync(ct),然后,在退出信号陷阱中,对于Windows它将是

    private static readonly CancellationTokenSource cts = new CancellationTokenSource();
    public static int Main(string[] args)
    {
        // For gracefull shutdown, trap unload event
        AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
        {
            cts.Cancel();
            exitEvent.Wait();
        };

        Console.CancelKeyPress += (sender, e) =>
        {
            cts.Cancel();
            exitEvent.Wait();
        };

        host.RunAsync(cts);
        Console.WriteLine("Shutting down");
        exitEvent.Set();
        return 0;
     }

...

控制台。CancelKeyPress事件用于此。下面是它的用法:

public static void Main(string[] args)
{
    Console.CancelKeyPress += delegate {
        // call methods to clean up
    };

    while (true) {}
}

当用户按Ctrl+C时,委托中的代码将运行,程序将退出。这允许您通过调用必要的方法来执行清理。请注意,执行委托之后没有代码。

在其他情况下,这是行不通的。例如,如果程序当前正在执行不能立即停止的重要计算。在这种情况下,正确的策略可能是告诉程序在计算完成后退出。下面的代码给出了如何实现的示例:

class MainClass
{
    private static bool keepRunning = true;

    public static void Main(string[] args)
    {
        Console.CancelKeyPress += delegate(object? sender, ConsoleCancelEventArgs e) {
            e.Cancel = true;
            MainClass.keepRunning = false;
        };
        
        while (MainClass.keepRunning) {
            // Do your work in here, in small chunks.
            // If you literally just want to wait until Ctrl+C,
            // not doing anything, see the answer using set-reset events.
        }
        Console.WriteLine("exited gracefully");
    }
}

这段代码与第一个示例之间的区别在于,e.Cancel被设置为true,这意味着在委托之后继续执行。如果运行,程序等待用户按Ctrl+C。当这种情况发生时,keepprunning变量会改变值,从而导致while循环退出。这是一种使程序优雅退出的方法。

我想补充一下乔纳斯的回答。在bool上旋转将导致100%的CPU利用率,并且在等待CTRL+C时浪费大量的能量。

更好的解决方案是使用ManualResetEvent来实际“等待”CTRL+C:

static void Main(string[] args) {
    var exitEvent = new ManualResetEvent(false);

    Console.CancelKeyPress += (sender, eventArgs) => {
                                  eventArgs.Cancel = true;
                                  exitEvent.Set();
                              };

    var server = new MyServer();     // example
    server.Run();

    exitEvent.WaitOne();
    server.Stop();
}

检测SIGTERM和Ctrl+C:

CancellationTokenSource ctSource = new();
CancellationToken ct = ctSource.Token;

void ExitHandler()
{
    // You can add any arbitrary global clean up
    Console.WriteLine("Exiting...");
    ctSource.Cancel();
}

// Assign exit handler to be called when the process is terminated
// or the user hits CTRL+C
AppDomain.CurrentDomain.ProcessExit += (sender, args) => ExitHandler();
Console.CancelKeyPress += (sender, args) => ExitHandler();

// Then you can use the cancellation token to check for exit:
Console.WriteLine("Ready to gracefully shut down!");
while (!ct.IsCancellationRequested)
{
    Console.WriteLine($"Exit not detected, waiting another 10s.");
    Task.Delay(10000, ct).Wait(ct);
}