4

在 C++11 程序中,我有一些函数必须在每次收到给定的 unix 信号时尽快停止(并返回给调用者)。乍一看,仅在调用者函数中接收和拦截信号时抛出的异常似乎是显而易见的解决方案。

void sighandler(int sig) {
   throw new myexc();
}

void caller(void) {
    try {
        callee1();
        callee2();
    } catch (myexc e) {
        ...
    }
}

但是安全、可移植的信号处理相当有限,因为更改 volatile sig_atomic_t 的值似乎是信号处理程序中唯一正确的事情。但我不想让我的代码到处都是检查 sig_atomic_t 是否已更改的测试。

void sighandler(int sig) {
    vol = 1;
}

void callee1(void) {
    do_stuff();
    if (vol == 1) return;
    do_other_stuff();
    if (vol == 1) return;
    do_something_again();
    ...
}

让一个线程等待值被更改然后抛出一个异常被另一个线程捕获似乎也不是一个有效的解决方案。

我怎样才能以安全、便携和优雅的方式做到这一点?

4

3 回答 3

4

您不能从信号处理程序中可移植地抛出异常。

并非所有操作系统实际上都具有堆栈展开所需的适当堆栈帧。您可以做的最好的事情是设置一个标志,然后在您的应用程序中对此进行测试。

为了防止您在代码中添加测试。
您可以将您的应用程序放入一个Reactor然后注册反应堆的实际工作。在反应器进行一项新工作之前,它会测试是否已设置信号标志。

 Reactor   workList;

 workList.add(&callee1);
 workList.add(&callee2);

 workList.run();

然后在里面Reactor;

 while(notSignalled() && !list.empty());
 {
     list.head().run();
 }
于 2012-06-15T15:57:44.987 回答
2

您确定在信号处理程序中允许抛出异常吗?它们完全独立于程序流程。这也是一个糟糕的设计——你应该只在非常特殊的情况下使用异常。

此外,全粒度callee1是必须的吗?很可能该函数大部分时间只花在几块代码上,因此不需要对返回条件进行大量检查。

于 2012-06-15T15:34:24.600 回答
1

你不能。通常,确保线程迅速终止的唯一方法是立即终止包含它的进程。(Posix 在信号中允许的内容比 C 标准多一点:例如,您可以调用abort()or _exit()。但不能exit()。)否则...信号是异步的,并且可能存在某些内部数据结构(例如堆栈帧)不处于连贯状态。

于 2012-06-15T16:06:28.590 回答