win下,当.NET控制台进程被强制终止(如关闭控制台、任务管理器结束进程等)时,如何优雅地清理数据

win下,当.NET控制台进程被强制终止(如关闭控制台、任务管理器结束进程等)时,如何优雅地清理数据

在Windows下,当.NET控制台进程被强制终止时,优雅地清理数据是一个挑战。以下是几种处理方案,按推荐程度排序:

1. 最佳方案:结合多种事件处理

csharp 复制代码
using System;
using System.Runtime.InteropServices;
using System.Threading;

class Program
{
    // Windows API 定义
    [DllImport("Kernel32")]
    private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
    
    private delegate bool EventHandler(CtrlType sig);
    private 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 ConsoleCtrlCheck(CtrlType ctrlType)
    {
        Console.WriteLine($"接收到关闭信号: {ctrlType}");
        Cleanup();
        return true; // 返回true表示已处理
    }
    
    static void Main(string[] args)
    {
        // 注册控制台控制事件处理器
        _handler += new EventHandler(ConsoleCtrlCheck);
        SetConsoleCtrlHandler(_handler, true);
        
        // 注册进程退出事件
        AppDomain.CurrentDomain.ProcessExit += (s, e) => Cleanup();
        
        // 注册AppDomain卸载事件
        AppDomain.CurrentDomain.DomainUnload += (s, e) => Cleanup();
        
        // 对于需要取消的循环操作,使用CancellationToken
        var cts = new CancellationTokenSource();
        Console.CancelKeyPress += (sender, e) =>
        {
            Console.WriteLine("Ctrl+C 被按下");
            e.Cancel = true; // 防止立即退出
            cts.Cancel();
            Cleanup();
        };
        
        try
        {
            // 主程序逻辑
            RunApplication(cts.Token);
        }
        finally
        {
            // 正常退出时的清理
            Cleanup();
        }
    }
    
    static void RunApplication(CancellationToken token)
    {
        // 示例:使用CancellationToken来响应取消请求
        while (!token.IsCancellationRequested)
        {
            Console.WriteLine("程序运行中... 按Ctrl+C退出");
            Thread.Sleep(1000);
        }
    }
    
    static void Cleanup()
    {
        // 使用标志避免重复清理
        if (_isCleaned) return;
        _isCleaned = true;
        
        try
        {
            Console.WriteLine("开始清理资源...");
            
            // 1. 保存数据到文件
            SaveCriticalData();
            
            // 2. 关闭数据库连接等资源
            CloseDatabaseConnections();
            
            // 3. 释放文件锁等
            ReleaseFileLocks();
            
            // 4. 记录日志
            LogShutdown();
            
            Console.WriteLine("清理完成");
            
            // 等待所有异步操作完成
            Thread.Sleep(500); // 给清理操作一点时间
        }
        catch (Exception ex)
        {
            Console.WriteLine($"清理过程中发生错误: {ex.Message}");
        }
    }
    
    private static bool _isCleaned = false;
    
    // 示例清理方法
    static void SaveCriticalData()
    {
        try
        {
            // 保存关键数据到文件
            File.WriteAllText("backup.txt", DateTime.Now.ToString());
        }
        catch { /* 忽略错误 */ }
    }
    
    static void CloseDatabaseConnections()
    {
        // 关闭数据库连接
    }
    
    static void ReleaseFileLocks()
    {
        // 释放文件锁
    }
    
    static void LogShutdown()
    {
        // 记录关闭日志
    }
}

2. 备用方案:使用子进程监控

对于无法捕获的终止(如任务管理器强制结束),可以创建一个监控进程:

csharp 复制代码
// 监控程序(父进程)
class MonitorProgram
{
    static void Main()
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "YourConsoleApp.exe",
                UseShellExecute = false
            }
        };
        
        process.Start();
        
        // 监控子进程退出
        process.WaitForExit();
        
        // 子进程退出后执行清理
        if (process.ExitCode != 0)
        {
            PerformEmergencyCleanup();
        }
    }
    
    static void PerformEmergencyCleanup()
    {
        // 紧急清理逻辑
    }
}

3. 防御性编程策略

3.1 定期保存状态

csharp 复制代码
class ProgramWithAutoSave
{
    private static Timer _autoSaveTimer;
    private static readonly object _saveLock = new object();
    
    static void Main()
    {
        // 每隔5分钟自动保存
        _autoSaveTimer = new Timer(_ =>
        {
            lock (_saveLock)
            {
                SaveCurrentState();
            }
        }, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
        
        // 主程序逻辑
    }
    
    static void SaveCurrentState()
    {
        // 保存当前状态到临时文件
        var tempFile = $"autosave_{DateTime.Now:yyyyMMddHHmmss}.tmp";
        // ... 保存逻辑
    }
}

3.2 使用文件锁确保数据一致性

csharp 复制代码
class ProgramWithFileLock
{
    private static FileStream _lockFile;
    
    static void Main()
    {
        try
        {
            // 创建锁文件
            _lockFile = File.Open("app.lock", 
                FileMode.OpenOrCreate, 
                FileAccess.ReadWrite, 
                FileShare.None);
            
            // 程序主逻辑
            RunProgram();
        }
        finally
        {
            _lockFile?.Close();
            File.Delete("app.lock");
        }
    }
    
    static void CheckForPreviousCrash()
    {
        if (File.Exists("app.lock"))
        {
            // 上次程序异常退出
            RecoverFromCrash();
        }
    }
}

4. Windows服务替代方案

如果可靠性要求很高,考虑使用Windows服务:

csharp 复制代码
using System.ServiceProcess;

public class MyService : ServiceBase
{
    protected override void OnStart(string[] args)
    {
        // 启动逻辑
    }
    
    protected override void OnStop()
    {
        // 优雅停止
        Cleanup();
    }
    
    protected override void OnShutdown()
    {
        // 系统关机时调用
        Cleanup();
        base.OnShutdown();
    }
}

5. 重要注意事项

  1. 清理时间限制:Windows在进程终止时只给大约30秒的时间进行清理
  2. 避免阻塞操作:清理操作应该快速完成
  3. 幂等性设计:清理操作应该可以安全地重复执行
  4. 关键数据立即持久化:重要数据应该立即写入磁盘,而不是缓存在内存中

推荐的最佳实践组合:

csharp 复制代码
// 简化的最佳实践示例
class RobustConsoleApp
{
    static void Main()
    {
        // 1. 设置控制台事件处理器
        SetConsoleHandlers();
        
        // 2. 检查上次是否异常退出
        CheckForCrashRecovery();
        
        // 3. 使用using确保资源释放
        using (var criticalResource = new CriticalResource())
        using (var cts = new CancellationTokenSource())
        {
            // 4. 设置取消令牌
            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                cts.Cancel();
            };
            
            // 5. 主程序循环
            try
            {
                MainLoop(cts.Token);
            }
            finally
            {
                Cleanup();
            }
        }
    }
}

重要提示:没有任何方法可以100%保证在强制终止时执行清理代码。最好的策略是:

  1. 设计应用程序使其可以容忍突然终止
  2. 定期保存状态到持久化存储
  3. 提供崩溃恢复机制
  4. 使用事务性操作保证数据一致性
相关推荐
时光追逐者11 分钟前
一款基于 .NET Avalonia 开源免费、快速、跨平台的图片查看器
c#·.net·图片查看器
酩酊仙人1 小时前
.Net机器学习入门
人工智能·机器学习·.net
CodeCraft Studio4 小时前
如何借助TeeChart图表库,实现放射治疗QA数据的精准可视化
信息可视化·.net·数据可视化·teechart·医疗软件·医疗数据分析·医用图表
老骥伏枥~13 小时前
VB.NET 中的单例模式
单例模式·.net
云草桑16 小时前
.net AI开发04 第八章 引入RAG知识库与文档管理核心能力及事件总线
数据库·人工智能·microsoft·c#·asp.net·.net·rag
Eiceblue16 小时前
.NET框架下Windows、Linux、Mac环境C#打印PDF全指南
linux·windows·.net
老骥伏枥~20 小时前
VB.NET 中的委托(Delegate)
.net
晚风吹长发1 天前
初步了解Linux中的信号捕捉
linux·运维·服务器·c++·算法·进程·x信号
云草桑1 天前
.net AI开发05 第九章 新增 RAG 文档处理后台服务 RagWorker 及核心流程
人工智能·ai·.net·rag·qdrant
缺点内向1 天前
Word 自动化处理:如何用 C# 让指定段落“隐身”?
开发语言·c#·自动化·word·.net