Saya ingin dapat menjebak CTRL+ Cdi aplikasi konsol C # sehingga saya dapat melakukan beberapa pembersihan sebelum keluar. Apa cara terbaik untuk melakukan ini?
Saya ingin dapat menjebak CTRL+ Cdi aplikasi konsol C # sehingga saya dapat melakukan beberapa pembersihan sebelum keluar. Apa cara terbaik untuk melakukan ini?
Jawaban:
Lihat MSDN:
Artikel dengan contoh kode:
Acara Console.CancelKeyPress digunakan untuk ini. Ini adalah bagaimana ini digunakan:
public static void Main(string[] args)
{
Console.CancelKeyPress += delegate {
// call methods to clean up
};
while (true) {}
}
Ketika pengguna menekan Ctrl + C kode dalam delegasi dijalankan dan program keluar. Ini memungkinkan Anda untuk melakukan pembersihan dengan memanggil metode yang diperlukan. Perhatikan bahwa tidak ada kode setelah delegasi dieksekusi.
Ada situasi lain di mana ini tidak akan memotongnya. Misalnya, jika program saat ini melakukan perhitungan penting yang tidak dapat segera dihentikan. Dalam hal itu, strategi yang tepat mungkin adalah memberi tahu program untuk keluar setelah perhitungan selesai. Kode berikut memberikan contoh bagaimana ini dapat diterapkan:
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");
}
}
Perbedaan antara kode ini dan contoh pertama adalah yang e.Cancel
disetel ke true, yang berarti eksekusi berlanjut setelah delegasi. Jika dijalankan, program menunggu pengguna menekan Ctrl + C. Ketika itu terjadi, keepRunning
nilai perubahan variabel yang menyebabkan loop sementara keluar. Ini adalah cara untuk membuat program keluar dengan anggun.
keepRunning
mungkin perlu ditandai volatile
. Utas utama mungkin menyimpannya di register CPU jika tidak, dan tidak akan melihat perubahan nilai ketika delegasi dieksekusi.
ManualResetEvent
spin daripada bool
.
winpty dotnet run
). Kalau tidak, delegasi tidak akan pernah dijalankan.
Saya ingin menambahkan jawaban Jonas . Berputar pada a bool
akan menyebabkan utilisasi CPU 100%, dan membuang banyak energi tanpa melakukan apa-apa sambil menunggu CTRL+ C.
Solusi yang lebih baik adalah menggunakan a ManualResetEvent
untuk benar-benar "menunggu" untuk 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();
}
Ini adalah contoh kerja yang lengkap. rekatkan ke proyek konsol C # kosong:
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..");
}
}
}
Pertanyaan ini sangat mirip dengan:
Inilah cara saya memecahkan masalah ini, dan menangani pengguna yang memukul X serta Ctrl-C. Perhatikan penggunaan ManualResetEvents. Ini akan menyebabkan thread utama tertidur yang membebaskan CPU untuk memproses thread lain sambil menunggu keluar, atau pembersihan. CATATAN: Perlu untuk mengatur TerminationCompletedEvent di akhir main. Kegagalan untuk melakukan hal itu menyebabkan latensi yang tidak perlu dalam penghentian karena waktu OS habis sementara membunuh aplikasi.
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;
}
}
}
Saya dapat melakukan beberapa pembersihan sebelum keluar. Apa cara terbaik untuk melakukan ini? Itulah tujuan sebenarnya: perangkap keluar, untuk membuat barang-barang Anda sendiri. Dan jawaban yang lebih baik di atas tidak memperbaikinya. Karena, Ctrl + C hanyalah salah satu dari banyak cara untuk keluar dari aplikasi.
Apa yang ada di dotnet c # diperlukan untuk itu - yang disebut token pembatalan diteruskan ke Host.RunAsync(ct)
dan kemudian, dalam perangkap sinyal keluar, untuk Windows akan menjadi
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
hanya disebutkan secara singkat di komentar. Artikel yang bagus adalah codeneverwritten.com/2006/10/…