【问题标题】:Cleanup code in Win32 console programWin32控制台程序中的清理代码
【发布时间】:2013-09-17 10:18:33
【问题描述】:

本题基于以下问题:Handle CTRL+C on Win32

我正在开发一个多线程服务器,在 Linux 和 Windows 上运行。我不能使用 boost 或其他框架,只能使用 std c++。

我在 win32 端的清理代码有问题。 linux端工作正常:当我想关闭服务器时,我发送SIGINT(带有CTRL+C),信号处理程序设置一个全局变量,主pthread执行清理指令(加入其他pthread,释放堆内存等)。

在 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;
}

输出如下:

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

所以主线程很快就被杀死了,只在两条指令之后。在上一个问题中one of the suggestion 是在处理程序中移动清理代码,但并没有真正帮助:

假设处理函数如下所示:

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;
} 

现在行为更糟了!输出是:

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

根据MSDN看来,进程总是被杀死:

一个 HandlerRoutine 可以执行任何必要的清理,然后取其中之一 以下操作:

  • 调用ExitProcess 函数终止进程。
  • 返回 FALSE。如果所有注册的处理程序函数都没有返回 TRUE,则默认处理程序终止进程。
  • 返回真。在这种情况下,不会调用其他处理函数并且系统终止

过程。

我是否遗漏了一些明显的东西? 终止 win32 控制台进程执行其清理代码的正确方法是什么?

【问题讨论】:

  • 你确定你看到的问题与cout 没有刷新有关吗,基于你从清理中返回true 的事实,所以代码终止而不调用您通常在exit 中看到的刷新?
  • 刚刚把'\n'换成std::endl,输出是一样的。
  • 那我恐怕没有进一步的建议了。
  • 恐怕你有点误导。关于即将关闭的处理程序 proc 返回值的文档是 not 用于 ctrl-c 处理的。它适用于CTRL_CLOSE_EVENTCTRL_LOGOFF_EVENTCTRL_SHUTDOWN_EVENT。常规的 ctrl-C 处理程序在处理 ctrl-c、接收通知和设置适当的正常关闭方面应该没有问题。如果有机会,我会尝试为您制作一个样本。
  • @MatsPetersson:解决方案是让处理程序保持运行(等待)直到主要完成。如链接问题中所述,当处理程序返回时,它会杀死它。不过,您只有 10 个,这是在 Windows 7+ 中添加的。

标签: c++ winapi console-application


【解决方案1】:

在 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 映射到SIGINT,就像在 Linux 上一样。

但是,这不会处理用户使用鼠标关闭控制台窗口。

【讨论】:

  • 这里也一样,看来主线程也用“unix”的处理方式很快就被杀掉了。
【解决方案2】:

这是一种方法,但我建议您使用事件 HANDLE 和 WaitForSingleObject,因为它往往会更加“屈服”。我把高速自旋循环留在这里只是为了让你在仍然看到处理程序被拦截的同时锁定你的一个核心。

我冒昧地修改了您的运行状态以分别进行原子评估和设置,因为我不希望优化器在主循环中抛出 eval。

#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;
}

输出(注意:我按了 ctrl-C,以防不明显)

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

注意:我在 64 位和 32 位进程的调试 版本中对此进行了测试,没有问题。您可以从 VS 调试器运行它。如果您安装了处理程序,则只需在通知您可以继续时选择“继续”即可。

【讨论】:

  • 我不确定你所说的“钉住你的一个核心”是什么意思,但是这段代码的问题是一样的:主线程在第二次迭代后被杀死,我在原始问题的代码中使用。
  • @ital 我很确定我知道为什么会这样。通过将 running=0 prior 设置为 for 循环,您将指示 main() 关闭。由于处理程序在不同的线程中运行(文档说它会这样做),因此您在std::cout 周围引入了生命周期并发问题。该标准要求它仅在main() 的生命周期内可用。一旦main()returns 在主线程上,RT-lib 将关闭它。在您的代码中,按照我上面的模型,当您将 running 设置为零 after 您的 for 循环而不是 before 时会发生什么?
  • 刚刚测试过(处理程序中的for循环和之后的running=false),不幸的是行为没有改变:前两个cout没问题,第三个停在中间.
  • @ital 好的,有些不同。您能否将您当前的代码按原样发布在您问题的更新中,最后用 UPDATE 标记以将其与您的其他尝试区分开来。我一定是在你的代码中遗漏了一些东西,或者你在我的代码中遗漏了一些东西。
猜你喜欢
  • 1970-01-01
  • 2018-04-28
  • 2010-11-04
  • 2013-04-14
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-15
相关资源
最近更新 更多