为了简化说明,我简化了示例。我正在编写一个数量为100的应用程序,但是在任何给定时间,我都允许用户通过键盘输入ctrl+c
来取消该程序。
由于我对函数指针的缺乏了解,看似简单程序的东西很快变得复杂起来。这就是我正在尝试做的事情:
ctrl+c
时捕获SIGINT信号。要注意的是,与Michael Haidl和Grijesh Chauhan在捕获SIGINT
时给出的两个示例不同,我不允许存储任何全局变量。理想的情况是将与signal()
相关的所有变量和函数调用封装在我的一类中。
这是我根据 Haidl 和 Grijesh的代码进行的修改尝试:
#include <thread>
#include <chrono>
#include <functional>
#include <iostream>
#include <signal.h>
class MyClass {
public:
volatile sig_atomic_t cancel = 0;
void sig_handler(int signal) {
cancel = true;
this->libCancel();
}
void libCancel() { std::cout << "Cancel and cleanup" << std::endl; }
};
int main(int argc, char *argv[]) {
MyClass mc;
//using std::placeholders::_1;
//std::function<void(int)> handler = std::bind(&MyClass::sig_handler, mc, _1);
//signal(SIGINT, handler);
signal(SIGINT, &mc.sig_handler); // **compiler error**
for (int i = 0; !mc.cancel && i < 100; ++i)
{
std::cout << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return 0;
}
如您所见,我希望代码简单地计数到100,如果一切顺利,则退出。但是,如果用户调用ctrl+c
,则该类应处理SIGINT
,调用外部库进行清理,然后for
循环将退出。
主要问题是我似乎无法设置signal()
声明来绑定到我的MyClass::sig_handler
实例。我甚至尝试将成员函数强制转换为std::function
以供signal()
使用,但已被注释掉,但是编译器对于C ++ function<void(int)>
不等同于C的事实并不满意。 lang void (*)(int)
。
欢迎任何批评。我完全不依赖于所写的内容,而且我对如何将函数指针与成员函数一起使用也没有很深入的了解。
答案 0 :(得分:4)
使用局部变量无法在信号处理程序和程序的其余部分之间进行通信。除了引发信号外,没有参数传递到处理程序中,并且处理程序不返回任何值。
“全局变量”一词有些含糊。人们有时根据上下文的含义不同。如果您的限制仅适用于全局范围,则只需在某些命名空间中使用volatile sig_atomic_t
。或者,如果愿意,可以使用静态成员变量。
如果您的限制适用于静态存储持续时间,则可以改用线程局部变量。
如果您的限制适用于所有全局内存,那么使用信号处理程序将无法解决您的问题。您只需要某种全局变量即可。
如果您可以依靠POSIX而不是C ++标准,则不使用全局变量来处理SIGINT
的一种方法是确保它不是 处理的,并使用{{1 }}。如果调用返回sigwait
,则停止程序,否则对捕获的信号执行您想做的事情。
当然,这意味着阻塞线程除了等待信号外不执行其他任何操作。您需要在其他线程中进行实际工作。
尽管从技术上讲,全局内存可能仍在使用。使用只是隐藏在系统库中。
此外,在信号处理程序中使用SIGINT
是不安全的。我知道这只是一个例子,但是“调用外部库进行清理” 很可能也是异步信号不安全的原因。
只需在for循环外部而不是在处理程序内部调用清除即可解决此问题。
主要问题是我似乎无法设置
std::cout
声明来绑定到我的signal()
实例。
这是因为MyClass::sig_handler
需要一个函数指针(类型为signal
)。非静态成员函数不能由函数指针指向。它们只能由成员函数指针指向,void(int)
不接受。