c# - 使 .NET 控制台应用程序保持事件状态,直到终止序列完成

标签 c# .net terminate windows-console kernel32

我正在开发一个数据采集应用程序,我想确保它正常退出。也就是说,它处理所有已收集的数据,将所有(文件)缓冲区刷新到“磁盘”(持久内存),甚至可能将数据上传到云端。

因此,我编写了(基于 this 答案)下面的代码来捕获每个关闭事件。 (这只是一个测试代码。)

问题:如果我使用控制台右上角的 X,程序会在短暂延迟后终止,即使终止序列仍在运行。 (处理程序确实被调用,它确实开始等待线程加入,但在一段时间后它被杀死。)如果我用 Crt+C 或 Ctr+Break 终止,它会按预期工作;终止序列完成并退出进程。

问题:如何让操作系统等待我的应用程序终止,而不是在短暂的宽限期后将其终止?

#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, List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
    //starts new foregeound thread, so the process doesn't terminate when all the cancelled threads end
    Thread closer = new Thread(() => terminationSequence(threads, tasks, cancellationRequest));
    closer.IsBackground = false;
    closer.Start();

    closer.Join();  //wait for the termination sequence to finish

    return true; //just to be pretty; this never runs (obviously)
}
private static void terminationSequence(List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
    cancellationRequest.Cancel(); //sends cancellation requests to all threads and tasks

    //wait for all the tasks to meet the cancellation request
    foreach (Task task in tasks)
    {
        task.Wait();
    }

    //wait for all the treads to meet the cancellation request
    foreach (Thread thread in threads)
    {
        thread.Join();
    }
    /*maybe do some additional work*/
    //simulate work being done
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    Console.WriteLine("Spinning");
    while (stopwatch.Elapsed.Seconds < 30)
    {
        if (stopwatch.Elapsed.Seconds % 2 == 0)
        {
            Console.Clear();
            Console.WriteLine("Elapsed Time: {0}m {1}s", stopwatch.Elapsed.Minutes, stopwatch.Elapsed.Seconds);
        }
        Thread.SpinWait(10000);
    }

    Environment.Exit(0); //exit the process
}
#endregion

static void Main(string[] args)
{
    CancellationTokenSource cancellationRequest = new CancellationTokenSource();    //cancellation signal to all threads and tasks
    List<Thread> threads = new List<Thread>(); //list of threads

    //specifys termination handler
    _handler += new EventHandler((type) => Handler(type, threads, new List<Task>(), cancellationRequest));
    SetConsoleCtrlHandler(_handler, true);

    //creating a new thread
    Thread t = new Thread(() => logic(cancellationRequest.Token));
    threads.Add(t);
    t.Start();
}

最佳答案

从 C# 7.1 开始,您可以使用 async Task Main() 方法。使用它,您可以修改您的处理程序以创建一个方法,并在 Main 方法中等待它。

Sidenote: you should use tasks instead of threads wherever you can. Tasks manage your thread better, and they run of the ThreadPool. When you create a new Thread instance, it's assuming it'll be a long running task, and the windows will treat it differently.

考虑到这一点,考虑将 TerminateSequence 方法包装在任务中,而不是线程中,并使该任务成为您的类的成员。现在您不必在处理程序的上下文中等待它,而是可以在 Main 方法中等待它。

在其余代码保持不变的情况下,您可以执行以下操作:

private Task _finalTask;

private static bool Handler(CtrlType sig, List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
    //starts new foregeound thread, so the process doesn't terminate when all the cancelled threads end
    _finalTask = Task.Run(() => terminationSequence(threads, tasks, cancellationRequest));
}

// ...

static async Task Main(string[] args)
{
    // ...

    // Wait for the termination process
    if(_finalProcess != null)
        await _finalTask
}

如果您不使用 C# 7.1,您仍然可以这样做,只是不够优雅。您需要做的就是等待它:

_finalTask?.Wait();

那应该就可以了。

关于c# - 使 .NET 控制台应用程序保持事件状态,直到终止序列完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49220493/

相关文章:

德尔福。如何知道 TEvent 是否已发出信号?

c# - .NET DLL 中的 System.DllNotFoundException

c# - 为什么 LinkedList 通常比 List 慢?

c# - 使用 OpenNLP 库在 C# 中创建 POSModel 对象

c# - 重新计算其他表单值 : textbox_Leave OR textbox_TextChanged?

c# - 最佳方法建议 : C# based Windows service implementation

c++ - 在没有 coredump 或 Segmentation Faults 的情况下退出程序

java - 我怎样才能停止我的申请?

c# - 将新的 Dispatcher 绑定(bind)到 WPF 中的线程

c# - 在 Windows 10 (1803) 上,如果 WPF 透明窗口覆盖在所有应用程序上,所有应用程序都会失去触摸或手写笔