【问题标题】:In C++11, how can I immediately leave a function when a signal is caught?在 C++11 中,如何在捕获信号时立即离开函数?
【发布时间】:2012-06-18 16:46:48
【问题描述】:

在 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();
    ...
}

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

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

【问题讨论】:

  • 也许使用 boost 线程,它们是可中断的
  • 也许使用“siglongjmp”(但这不会清理 c++ 类对象...)。

标签: c++ multithreading exception c++11


【解决方案1】:

您不能从信号处理程序中便携式地抛出异常。

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

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

 Reactor   workList;

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

 workList.run();

然后在Reactor里面;

 while(notSignalled() && !list.empty());
 {
     list.head().run();
 }

【讨论】:

  • 非常感谢;这可能是解决我的问题的一种干净的方法。
【解决方案2】:

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

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

【讨论】:

  • 没错,这里可能存在滥用异常;我不知道。其实在治疗过程中能接收到信号的实际情况是非常特殊的……好吧,反正这里例外是行不通的……
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-17
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2019-10-29
    相关资源
    最近更新 更多