C# 콘솔 앱에서 ctrl-c (sigint)를 어떻게 덫에 걸립니까
문제
나는 함정을 가질 수 있기를 원합니다 CTRL 키+씨 종료하기 전에 일부 정리를 수행 할 수 있도록 C# 콘솔 애플리케이션에서. 이것을하는 가장 좋은 방법은 무엇입니까?
해결책
다른 팁
그만큼 콘솔 .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를 누르기를 기다립니다. keepRunning
가변적 인 값을 변경하여 while 루프가 종료됩니다. 이것은 프로그램을 우아하게 빠뜨리는 방법입니다.
추가하고 싶습니다 조나스의 대답. a bool
100% CPU 이용을 유발하고 기다리는 동안 아무것도하지 않는 많은 에너지를 낭비합니다. CTRL 키+씨.
더 나은 해결책은 a를 사용하는 것입니다 ManualResetEvent
실제로 "기다려" CTRL 키+씨:
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..");
}
}
}
이 질문은 다음과 매우 유사합니다.
다음은이 문제를 해결하고 CTRL-C뿐만 아니라 X를 치는 사용자를 처리했습니다. ManualReseteVents의 사용에 주목하십시오. 이로 인해 기본 스레드가 잠을 자게하여 CPU가 종료되기를 기다리는 동안 CPU가 다른 스레드를 처리하거나 정리할 수 있습니다. 참고 : 메인 끝에 TerminationCompletedEvent를 설정해야합니다. 그렇게하지 않으면 응용 프로그램을 죽이는 동안 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;
}
}
}
Console.TreatControlCAsInput = true;
나를 위해 일했습니다.