在信号处理程序中使用互斥体

Yuk*_*uki 2 c++ linux multithreading signals pthreads

我有一个多线程应用程序,需要在中断、终止等信号时优雅地停止它。

这里有一个片段来说明逻辑的相关部分。

std::atomic_bool running = true;
std::mutex mutex;
std::condition_variable condition;

void signal_handler(int signal)
{
  // in another thread `[&]() { return !running || something_to_do_conditon; } == false`
  // what means continue to wait
  running = false;
  condition.notify_one();
  // another thread goes to sleep
}

void run() {
    while(true) {
      std::unique_lock lock(mutex);
      condition.wait_for(lock, std::chrono::hours(1), [&]() { return !running || something_to_do_conditon; });

      if (!running) {
          return;
      }

      // do smth
    }
}

int main()
{
  // Install a signal handler
  std::signal(SIGINT, signal_handler);
  std::signal(SIGTERM, signal_handler);

  std::thread thread(run);
  thread.join();
}
Run Code Online (Sandbox Code Playgroud)

正如您在可能出现的情况中看到的,signal_handler即使running设置为false,condition仍会收到通知,但仍然存在线程休眠 1 小时的情况(用内嵌注释进行描述)。发生这种情况是因为变量周围没有互斥体running。这允许线程锁定互斥体并在设置变量之前检查条件。如果我添加类似的东西

  {
      std::lock_guard<std::mutex> lock(mutex);
      running = false;
  }
Run Code Online (Sandbox Code Playgroud)

在处理程序中这是可以避免的。

那么问题是如何使用(是否可能)互斥体而不会出现潜在的死锁或任何其他问题。任何其他削弱睡眠线程信号的技巧。

caf*_*caf 5

在 pthreads 程序中处理信号的可靠方法是屏蔽您希望在每个线程中处理的所有信号,并创建一个围绕调用sigwaitinfo()(或sigtimedwait()) 循环的专用信号处理线程。

然后,信号处理线程可以使用普通的互斥锁保护共享变量和pthread_cond_signal()/pthread_cond_broadcast()唤醒来通知其他线程有关收到的信号。

在您的示例中,如果以这种方式编写,专用信号处理线程可以在更改标志之前安全地锁定互斥体running,因为它只是在普通线程上下文中而不是信号处理程序。

  • 信号处理程序中的互斥体不是一个好主意,因为如果出现另一个信号,它可能会重新进入处理程序,从而导致死锁。 (2认同)
  • @teoring:对 - 更一般地说,互斥函数不是异步信号安全的,因此在该上下文中使用它们是错误的。这个答案是关于在普通线程上下文中使用线程来使用“sigwaitinfo()”处理信号(而不是使用已安装的信号处理函数),这确实允许安全地使用互斥函数。 (2认同)