我的代码是这样的:
#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...
没有打印出“析构函数”。 那么,我该如何干净利落地退出?
答案 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; );
}