c++ 在不使用全局变量的情况下捕获 ctrl+c

c++ capture ctrl+c without using globals

为了便于解释,我简化了示例。我正在编写一个计数为 100 的应用程序,但在任何给定时间我都允许用户通过键盘输入 ctrl+c 来取消程序。

由于我对函数指针的了解不足,看似简单的程序很快就变得复杂起来。这就是我正在尝试做的事情:

  1. 在按下ctrl+c时捕获SIGINT信号。
  2. 捕获后,调用关闭第三方资源的成员函数。

要注意的是,与 Michael Haidl and Grijesh Chauhan 在捕获 SIGINT 时给出的两个示例不同,我不允许存储任何全局变量。理想的场景是所有与 signal() 相关的变量和函数调用都封装在我的 class 中。

这是我根据 HaidlGrijesh 的 代码修改后的尝试:

#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,那么 class 应该处理 SIGINT,调用外部库进行清理,for 循环将退出。

主要问题是我似乎无法设置 signal() 声明来绑定到我的 MyClass::sig_handler 实例。我什至尝试将我的成员函数转换为 std::function 以供 signal() 使用,注释掉了,但编译器对 C++ function<void(int)> 不等同于 C 的事实不满意朗 void (*)(int).

欢迎任何批评。我完全不受我所写内容的束缚,而且我显然对如何将函数指针与成员函数一起使用没有很好的基本理解。

无法使用局部变量在信号处理程序和程序的其余部分之间进行通信。除了引发的信号和处理程序 returns 没有值。

没有参数被传递到处理程序

"global variables"这几个字有点歧义。人们有时会根据上下文表达不同的意思。如果您的限制仅适用于全局范围,则只需在某个命名空间内使用 volatile sig_atomic_t。或者使用静态成员变量,如果你愿意的话。

如果您的限制适用于静态存储持续时间,那么您可以改用线程局部变量。

如果您的限制适用于所有全局内存,那么使用信号处理程序无法解决您的问题。您只需要某种全局变量。


如果你可以依赖 POSIX 而不是 C++ 标准,一种在没有全局变量的情况下处理 SIGINT 的方法是确保它 不是 处理,并用 sigwait 阻塞线程。如果调用 returns SIGINT,则停止程序,否则对捕获的信号执行您想执行的操作。

当然,这意味着阻塞线程除了等待信号外什么都不做。您需要在其他线程中完成实际工作。

但从技术上讲,可能仍在使用全局内存。使用只是隐藏在系统库中。


此外,在信号处理程序中使用 std::cout 是不安全的。我知道这只是一个例子,但是 "call the external library for cleanup" 很可能也是异步信号不安全。

这可以通过在 for 循环外部而不是在处理程序内部调用清理来简单地解决。


The main problem is that I can't seem to setup the signal() declaration to bind to my instance of MyClass::sig_handler.

那是因为 signal 需要一个函数指针(void(int) 类型)。 Non-static 函数指针不能指向成员函数。它们只能由成员函数指针指向,signal 不接受。