IT

C# 콘솔 앱에서 Ctrl+C(SIGINT)를 어떻게 트랩합니까?

itgroup 2023. 5. 13. 09:28
반응형

C# 콘솔 앱에서 Ctrl+C(SIGINT)를 어떻게 트랩합니까?

종료하기 전에 정리 작업을 수행할 수 있도록 C# 콘솔 응용 프로그램에 +를 C트랩할 수 있습니다.이것을 하는 가장 좋은 방법은 무엇입니까?

콘솔.CancelKeyPress 이벤트가 이에 사용됩니다.사용 방법은 다음과 같습니다.

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

    while (true) {}
}

사용자가 +를 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.Canceltrue로 설정되어 있습니다. 즉, 대리자 이후에도 실행이 계속됩니다.실행되면 프로그램은 사용자가 +C를 누를 때까지 기다립니다.그런 일이 발생합니다.keepRunningvariable은 값을 변경하여 while 루프를 종료합니다.이것은 프로그램을 정상적으로 종료하는 방법입니다.

MSDN 참조:

콘솔.키 누르기 이벤트 취소

코드 샘플이 포함된 기사:

Ctrl-C 및 를 누릅니다.NET 콘솔 응용 프로그램

조나스의 답변에 추가하고 싶습니다.위에서 회전boolCPU 활용률이 100%에 이르고 C+를 기다리는 동안 아무것도 하지 않고 많은 에너지를 낭비합니다.

더 나은 해결책은 다음을 사용하는 것입니다.ManualResetEvent+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();
}

다음은 완전한 작업 예제입니다. 빈 C# 콘솔 프로젝트에 붙여넣기:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace TestTrapCtrlC {
    public class Program {
        static bool exitSystem = false;

        #region Trap application termination
        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);

        private delegate bool EventHandler(CtrlType sig);
        static EventHandler _handler;

        enum CtrlType {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT = 1,
            CTRL_CLOSE_EVENT = 2,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT = 6
        }

        private static bool Handler(CtrlType sig) {
            Console.WriteLine("Exiting system due to external CTRL-C, or process kill, or shutdown");

            //do your cleanup here
            Thread.Sleep(5000); //simulate some cleanup delay

            Console.WriteLine("Cleanup complete");

            //allow main to run off
            exitSystem = true;

            //shutdown right away so there are no lingering threads
            Environment.Exit(-1);

            return true;
        }
        #endregion

        static void Main(string[] args) {
            // Some biolerplate to react to close window event, CTRL-C, kill, etc
            _handler += new EventHandler(Handler);
            SetConsoleCtrlHandler(_handler, true);

            //start your multi threaded program here
            Program p = new Program();
            p.Start();

            //hold the console so it doesn’t run off the end
            while (!exitSystem) {
                Thread.Sleep(500);
            }
        }

        public void Start() {
            // start a thread and start doing some processing
            Console.WriteLine("Thread started, processing..");
        }
    }
}

이 질문은 다음과 매우 유사합니다.

캡처 콘솔 종료 C#

제가 이 문제를 해결하고 Ctrl-C뿐만 아니라 X를 치는 사용자를 처리한 방법은 다음과 같습니다.수동 재설정 이벤트를 사용합니다.이렇게 하면 메인 스레드가 절전 모드로 전환되어 종료 또는 정리를 기다리는 동안 CPU가 다른 스레드를 처리할 수 있습니다.참고: 종료 완료 이벤트를 메인 끝에 설정해야 합니다.그렇지 않으면 애플리케이션을 종료하는 동안 OS가 시간 초과되어 불필요한 종료 대기 시간이 발생합니다.

namespace CancelSample
{
    using System;
    using System.Threading;
    using System.Runtime.InteropServices;

    internal class Program
    {
        /// <summary>
        /// Adds or removes an application-defined HandlerRoutine function from the list of handler functions for the calling process
        /// </summary>
        /// <param name="handler">A pointer to the application-defined HandlerRoutine function to be added or removed. This parameter can be NULL.</param>
        /// <param name="add">If this parameter is TRUE, the handler is added; if it is FALSE, the handler is removed.</param>
        /// <returns>If the function succeeds, the return value is true.</returns>
        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(ConsoleCloseHandler handler, bool add);

        /// <summary>
        /// The console close handler delegate.
        /// </summary>
        /// <param name="closeReason">
        /// The close reason.
        /// </param>
        /// <returns>
        /// True if cleanup is complete, false to run other registered close handlers.
        /// </returns>
        private delegate bool ConsoleCloseHandler(int closeReason);

        /// <summary>
        ///  Event set when the process is terminated.
        /// </summary>
        private static readonly ManualResetEvent TerminationRequestedEvent;

        /// <summary>
        /// Event set when the process terminates.
        /// </summary>
        private static readonly ManualResetEvent TerminationCompletedEvent;

        /// <summary>
        /// Static constructor
        /// </summary>
        static Program()
        {
            // Do this initialization here to avoid polluting Main() with it
            // also this is a great place to initialize multiple static
            // variables.
            TerminationRequestedEvent = new ManualResetEvent(false);
            TerminationCompletedEvent = new ManualResetEvent(false);
            SetConsoleCtrlHandler(OnConsoleCloseEvent, true);
        }

        /// <summary>
        /// The main console entry point.
        /// </summary>
        /// <param name="args">The commandline arguments.</param>
        private static void Main(string[] args)
        {
            // Wait for the termination event
            while (!TerminationRequestedEvent.WaitOne(0))
            {
                // Something to do while waiting
                Console.WriteLine("Work");
            }

            // Sleep until termination
            TerminationRequestedEvent.WaitOne();

            // Print a message which represents the operation
            Console.WriteLine("Cleanup");

            // Set this to terminate immediately (if not set, the OS will
            // eventually kill the process)
            TerminationCompletedEvent.Set();
        }

        /// <summary>
        /// Method called when the user presses Ctrl-C
        /// </summary>
        /// <param name="reason">The close reason</param>
        private static bool OnConsoleCloseEvent(int reason)
        {
            // Signal termination
            TerminationRequestedEvent.Set();

            // Wait for cleanup
            TerminationCompletedEvent.WaitOne();

            // Don't run other handlers, just exit.
            return true;
        }
    }
}

SIGTERM 및 +: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);
}

나가기 전에 정리 작업을 수행할 수 있습니다.이것을 하는 가장 좋은 방법은 진정한 목표입니다: 함정 출구, 당신만의 물건을 만드는 것입니다.그리고 그것을 올바르게 만들지 않는 것보다 더 나은 대답.왜냐하면 Ctrl+C는 앱을 종료하는 많은 방법 중 하나일 뿐이기 때문입니다.

dotnet c#의 내용 - 이른바 취소 토큰 전달 대상Host.RunAsync(ct)신호 는 Windows의 경우는 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;
     }

...

Console.TreatControlCAsInput = true;나를 위해 일했습니다.

언급URL : https://stackoverflow.com/questions/177856/how-do-i-trap-ctrlc-sigint-in-a-c-sharp-console-app

반응형