简体   繁体   English

Win32控制台程序中的清理代码

[英]Cleanup code in Win32 console program

This question is based on the following question: Handle CTRL+C on Win32 此问题基于以下问题: Win32上的CTRL + C处理

I'm working on a multithread server, running on Linux and Windows. 我正在运行在Linux和Windows上的多线程服务器上。 I can't use boost or other frameworks, only std c++. 我不能使用boost或其他框架,只能使用std c ++。

I have a problem with the cleanup code on the win32 side. 我在win32端的清理代码有问题。 The linux side is working fine: when I want to shutdown the server, I send SIGINT (with CTRL+C ), the signal handler sets a global variable and the main pthread executes the cleanup instructions (joining other pthreads, freeing heap memory, etc.). linux方面运行良好:当我想关闭服务器时,我发送SIGINT (使用CTRL+C ),信号处理程序设置了一个全局变量,并且主pthread执行清除指令(加入其他pthread,释放堆内存等) )。

On windows it looks not so simple to get the same behavior. 在Windows上,获得相同的行为看起来并不那么简单。 I have written a simple test program to understand how the signal handlers works in windows. 我编写了一个简单的测试程序,以了解信号处理程序在Windows中的工作方式。

#include <iostream>
#include <windows.h>

bool running;

BOOL WINAPI consoleHandler(DWORD signal) {

    if (signal == CTRL_C_EVENT) {
        running = false;
        std::cout << "[CTRL+C]\n";
        return TRUE;
    }

    return FALSE;
} 

int main(int argc, char **argv) {

    running = true;

    if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {

            std::cerr << "Error: " << GetLastError() << '\n';
            return -1;
    }

    std::cout << "Main thread working hard...\n";
    while (running) { ; }

    for (int i = 0; i < 20; i++)
        std::cout << "This is the " << i << "th fake cleanup instruction\n";

    return 0;
}

The output is the following: 输出如下:

$ test.exe
Main thread working hard...
[CTRL+C]
This is the 0th fake cleanup instruction
This is the 1th fake cleanup instruction

So the main thread is killed quickly, only after two instruction. 因此,只有在执行两次指令后,主线程才会被快速杀死。 In the previous question one of the suggestion was to move the cleanup code in the handler, but is not really helping: 在前面的问题中, 一个建议是在处理程序中移动清理代码,但实际上并没有帮助:

suppose that the handler function looks like this: 假设处理程序函数如下所示:

BOOL WINAPI consoleHandler(DWORD signal) {

    if (signal == CTRL_C_EVENT) {
        running = false;
        std::cout << "[CTRL+C]\n";

        for (int i = 0; i < 20; i++)    
            std::cout << "This is the " << i << "th fake cleanup instruction\n";

        return TRUE;
    }

    return FALSE;
} 

Now the behavior is even worse! 现在的行为更糟​​! The output is: 输出为:

$ test.exe
Main thread working hard...
[CTRL+C]
This is the

According to MSDN , it seems that the process is always killed: 根据MSDN ,该过程似乎总是被杀死:

A HandlerRoutine can perform any necessary cleanup, then take one of the following actions: HandlerRoutine可以执行任何必要的清除,然后执行以下操作之一:

  • Call the ExitProcess function to terminate the process. 调用ExitProcess函数终止该过程。
  • Return FALSE. 返回FALSE。 If none of the registered handler functions returns TRUE, the default handler terminates the process. 如果所有已注册的处理程序函数均未返回TRUE,则默认处理程序将终止该过程。
  • Return TRUE. 返回TRUE。 In this case, no other handler functions are called and the system terminates 在这种情况下,不会调用其他处理程序函数,并且系统将终止

the process. 过程。

Am I missing something obvious? 我是否缺少明显的东西? What's the proper way to terminate a win32 console process and executes its cleanup code? 终止win32控制台进程执行其清理代码的正确方法是什么?

This is one way to do it, though I would suggest you use an event HANDLE and WaitForSingleObject, as it would tend to be considerably more "yielding". 这是一种方法,尽管我建议您使用事件HANDLE和WaitForSingleObject,因为它通常会更“屈服”。 I left the high velocity spin-loop in this just for you to peg one of your cores while still seeing the handler is intercepted. 我在其中留下了高速自旋循环,只是为了让您钉住一个核心,同时仍然看到处理程序被拦截。

I took the liberty of modifying your running state to be atomically evaluated and set respectively, as I didn't want the optimizer throwing out the eval in the main loop. 我随意修改运行状态以分别进行原子评估和设置,因为我不希望优化器在主循环中抛出评估值。

#include <iostream>
#include <cstdlib>
#include <windows.h>

// using an event for monitoring
LONG running = 1;

BOOL WINAPI consoleHandler(DWORD signal)
{
    if (signal == CTRL_C_EVENT) 
    {
        std::out << "Received Ctrl-C; shutting down..." << std::endl;
        InterlockedExchange(&running, 0);
        return TRUE;
    }
    return FALSE;
} 

int main(int argc, char **argv) 
{
    if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) 
    {
        std::cerr << "Error: " << GetLastError() << '\n';
        return EXIT_FAILURE;
    }

    std::cout << "Main thread working hard...\n";
    while (InterlockedCompareExchange(&running, 0, 0) == 1);

    std::cout << "Graceful shutdown received. Shutting down now." << std::endl;

    return 0;
}

Output (note: I pressed ctrl-C, in case it wasn't obvious) 输出 (注意:如果不明显,我按了Ctrl-C)

Main thread working hard...
Received Ctrl-C; shutting down...
Graceful shutdown received. Shutting down now.

Note: I tested this in debug and release in both 64 and 32 bit processes, no issues. 注意:我在64位和32位进程的调试发布版本中对此进行了测试,没有问题。 And you can run it from the VS debugger. 可以从VS调试器运行它。 Just select "Continue" when informed you can continue if you have a handler installed, which you do. 通知您可以继续(如果已安装处理程序),只需选择“继续”即可。

On Windows you can use a signal handler as well: 在Windows上,您也可以使用信号处理程序:

static void shutdown(int signum)
{
  printf("got signal #%d, terminating\n", signum);
  // cleanup
  _exit(1);
}

signal(SIGINT, shutdown);
signal(SIGTERM, shutdown);
signal(SIGSEGV, shutdown);

Ctrl-C is mapped to SIGINT just like on Linux. 就像在Linux上一样,Ctrl-C映射到SIGINT

This won't handle the user closing the console window using mouse, however. 但是,这将无法处理用户使用鼠标关闭控制台窗口的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM