win下,当.NET控制台进程被强制终止(如关闭控制台、任务管理器结束进程等)时,如何优雅地清理数据
在Windows下,当.NET控制台进程被强制终止时,优雅地清理数据是一个挑战。以下是几种处理方案,按推荐程度排序:
1.最佳方案:结合多种事件处理
usingSystem;usingSystem.Runtime.InteropServices;usingSystem.Threading;classProgram{// Windows API 定义[DllImport("Kernel32")]privatestaticexternboolSetConsoleCtrlHandler(EventHandlerhandler,booladd);privatedelegateboolEventHandler(CtrlTypesig);privatestaticEventHandler_handler;enumCtrlType{CTRL_C_EVENT=0,CTRL_BREAK_EVENT=1,CTRL_CLOSE_EVENT=2,CTRL_LOGOFF_EVENT=5,CTRL_SHUTDOWN_EVENT=6}privatestaticboolConsoleCtrlCheck(CtrlTypectrlType){Console.WriteLine($"接收到关闭信号:{ctrlType}");Cleanup();returntrue;// 返回true表示已处理}staticvoidMain(string[]args){// 注册控制台控制事件处理器_handler+=newEventHandler(ConsoleCtrlCheck);SetConsoleCtrlHandler(_handler,true);// 注册进程退出事件AppDomain.CurrentDomain.ProcessExit+=(s,e)=>Cleanup();// 注册AppDomain卸载事件AppDomain.CurrentDomain.DomainUnload+=(s,e)=>Cleanup();// 对于需要取消的循环操作,使用CancellationTokenvarcts=newCancellationTokenSource();Console.CancelKeyPress+=(sender,e)=>{Console.WriteLine("Ctrl+C 被按下");e.Cancel=true;// 防止立即退出cts.Cancel();Cleanup();};try{// 主程序逻辑RunApplication(cts.Token);}finally{// 正常退出时的清理Cleanup();}}staticvoidRunApplication(CancellationTokentoken){// 示例:使用CancellationToken来响应取消请求while(!token.IsCancellationRequested){Console.WriteLine("程序运行中... 按Ctrl+C退出");Thread.Sleep(1000);}}staticvoidCleanup(){// 使用标志避免重复清理if(_isCleaned)return;_isCleaned=true;try{Console.WriteLine("开始清理资源...");// 1. 保存数据到文件SaveCriticalData();// 2. 关闭数据库连接等资源CloseDatabaseConnections();// 3. 释放文件锁等ReleaseFileLocks();// 4. 记录日志LogShutdown();Console.WriteLine("清理完成");// 等待所有异步操作完成Thread.Sleep(500);// 给清理操作一点时间}catch(Exceptionex){Console.WriteLine($"清理过程中发生错误:{ex.Message}");}}privatestaticbool_isCleaned=false;// 示例清理方法staticvoidSaveCriticalData(){try{// 保存关键数据到文件File.WriteAllText("backup.txt",DateTime.Now.ToString());}catch{/* 忽略错误 */}}staticvoidCloseDatabaseConnections(){// 关闭数据库连接}staticvoidReleaseFileLocks(){// 释放文件锁}staticvoidLogShutdown(){// 记录关闭日志}}2.备用方案:使用子进程监控
对于无法捕获的终止(如任务管理器强制结束),可以创建一个监控进程:
// 监控程序(父进程)classMonitorProgram{staticvoidMain(){varprocess=newProcess{StartInfo=newProcessStartInfo{FileName="YourConsoleApp.exe",UseShellExecute=false}};process.Start();// 监控子进程退出process.WaitForExit();// 子进程退出后执行清理if(process.ExitCode!=0){PerformEmergencyCleanup();}}staticvoidPerformEmergencyCleanup(){// 紧急清理逻辑}}3.防御性编程策略
3.1 定期保存状态
classProgramWithAutoSave{privatestaticTimer_autoSaveTimer;privatestaticreadonlyobject_saveLock=newobject();staticvoidMain(){// 每隔5分钟自动保存_autoSaveTimer=newTimer(_=>{lock(_saveLock){SaveCurrentState();}},null,TimeSpan.Zero,TimeSpan.FromMinutes(5));// 主程序逻辑}staticvoidSaveCurrentState(){// 保存当前状态到临时文件vartempFile=$"autosave_{DateTime.Now:yyyyMMddHHmmss}.tmp";// ... 保存逻辑}}3.2 使用文件锁确保数据一致性
classProgramWithFileLock{privatestaticFileStream_lockFile;staticvoidMain(){try{// 创建锁文件_lockFile=File.Open("app.lock",FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.None);// 程序主逻辑RunProgram();}finally{_lockFile?.Close();File.Delete("app.lock");}}staticvoidCheckForPreviousCrash(){if(File.Exists("app.lock")){// 上次程序异常退出RecoverFromCrash();}}}4.Windows服务替代方案
如果可靠性要求很高,考虑使用Windows服务:
usingSystem.ServiceProcess;publicclassMyService:ServiceBase{protectedoverridevoidOnStart(string[]args){// 启动逻辑}protectedoverridevoidOnStop(){// 优雅停止Cleanup();}protectedoverridevoidOnShutdown(){// 系统关机时调用Cleanup();base.OnShutdown();}}5.重要注意事项
- 清理时间限制:Windows在进程终止时只给大约30秒的时间进行清理
- 避免阻塞操作:清理操作应该快速完成
- 幂等性设计:清理操作应该可以安全地重复执行
- 关键数据立即持久化:重要数据应该立即写入磁盘,而不是缓存在内存中
推荐的最佳实践组合:
// 简化的最佳实践示例classRobustConsoleApp{staticvoidMain(){// 1. 设置控制台事件处理器SetConsoleHandlers();// 2. 检查上次是否异常退出CheckForCrashRecovery();// 3. 使用using确保资源释放using(varcriticalResource=newCriticalResource())using(varcts=newCancellationTokenSource()){// 4. 设置取消令牌Console.CancelKeyPress+=(s,e)=>{e.Cancel=true;cts.Cancel();};// 5. 主程序循环try{MainLoop(cts.Token);}finally{Cleanup();}}}}重要提示:没有任何方法可以100%保证在强制终止时执行清理代码。最好的策略是:
- 设计应用程序使其可以容忍突然终止
- 定期保存状态到持久化存储
- 提供崩溃恢复机制
- 使用事务性操作保证数据一致性