如何在c ++中处理中断信号并调用析构函数?

时间:2012-03-08 09:56:02

标签: c++ destructor signals

  

可能重复:
  Is destructor called if SIGINT or SIGSTP issued?

我的代码是这样的:

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

void handler(int) {
    std::cout << "will exit..." << std::endl;
    exit(0);
}

class A {
public:
    A() {std::cout << "constructor" << std::endl;}
    ~A() {std::cout << "destructor" << std::endl;}
};

int main(void) {
    signal(SIGINT, &handler);

    A a;
    for (;;);

    return 0;
}

当我按下Ctrl-C时,它打印出来:

constructor
^Cwill exit...

没有打印出“析构函数”。 那么,我该如何干净利落地退出?

5 个答案:

答案 0 :(得分:11)

有困难。您编写的代码已经未定义 行为;你不能输出信号处理程序中的流; 就此而言,您也不能拨打exit。 (我在做什么 我在Posix标准上的断言。在纯C ++中,你只需要 允许做的是分配给sig_atomic_t类型的变量。)

在类似代码的简单情况下,您可以执行以下操作:

sig_atomic_t stopFlag = 0;

void
handler( int )
{
    stopFlag = 1;
}

int
main()
{
    signal( SIGINT, &handler );
    A a;
    while ( stopFlag == 0 ) {
    }
    std::cout << "will exit..." << std::endl;
    return 0;
}

根据应用程序的不同,您可以执行以下操作: 在适当的地方检查stopFlag。但一般来说,如果你 试试这个,会有竞争条件:你之前检查stopFlag 启动一个可中断的系统调用,然后进行调用;信号 在支票和通话之间到达,你打电话,但事实并非如此 中断。 (我已经使用过这种技术,但是在一个应用程序中 只有可中断的系统调用是一个非常短的套接字读取 超时。)

通常,至少在Posix下,你最终必须创建一个信号 处理线程;这可以用来干净地关闭所有的 其他线程。基本上,您首先将信号掩码设置为阻止 所有信号,然后在信号处理线程中,一旦启动,将其设置为 接受您感兴趣的信号并致电sigwait()。这个 但是,这意味着您要执行所有必需的操作 干净的线程关闭:信号处理线程必须知道 关于所有其他线程,在它们上面调用pthread_cancel等等,你就是 编译器必须生成正确的代码来处理pthread_cancel或 你需要开发一些其他方法来确保所有线程都是 正确通知。 (今天,人们希望所有编译器都能处理 pthread_cancel正确。但是人们永远都不知道这样做了 显着的运行时成本,通常不需要。)

答案 1 :(得分:2)

无论如何都应该释放内存。 但是如果你有代码需要处理,我想你必须跟踪所有对象,然后根据需要销毁它们(例如让构造函数将它们添加到std::set,同时析构函数删除他们再次)。但是,这不能确保正确的破坏顺序(这可能需要一些更复杂的解决方案)。

您也可以使用信号处理程序设置一些标志,该标志将留下无限循环(或者您在主循环中执行的任何操作),而不是简单地使用exit()终止。

答案 2 :(得分:2)

您需要退出main函数的范围才能使析构函数正常工作:

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

bool stop = false;
void handler(int) {
    std::cout << "will exit..." << std::endl;
    stop = true;
}

class A {
public:
    A() {std::cout << "constructor" << std::endl;}
    ~A() {std::cout << "destructor" << std::endl;}
};

int main(void) {
  A a;
  signal(SIGINT, &handler);

  for (;!stop;);

  return 0;
}

答案 3 :(得分:2)

这是因为普通代码和信号处理程序的上下文不同。如果将变量a放在全局范围内(即在任何函数之外),您将看到析构函数被正确调用。

如果你想自己处理清理(而不是让运行时和操作系统处理它),你可以有一个条件循环,如下所示:

bool keep_running = true;

void handler(int) {
    std::cout << "will exit..." << std::endl;
    keep_running = false;
}

int main(void) {
    signal(SIGINT, &handler);

    A a;
    while (keep_running);

    return 0;
}

答案 4 :(得分:1)

exit几乎立即终止了这个过程;特别是,具有自动存储持续时间的对象不会被破坏。流也被刷新和关闭,但是不允许您从信号处理程序内部触摸流。所以......

不要从信号处理程序中调用exit;设置一些原子标志来指示循环结束。

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

sig_atomic_t exitRequested = 0;

void handler(int) {
    std::cout << "will exit..." << std::endl;
    exitRequested = 1;
}

struct A {
     A() { std::cout << "constructor" << std::endl; }
    ~A() { std::cout << "destructor" << std::endl; }
};

int main() {
    signal(SIGINT, &handler);

    A a;
    for (; !exitRequested; );
}