Использование мьютексов в обработчиках сигналов - PullRequest
0 голосов
/ 22 марта 2020

У меня есть многопоточное приложение, и мне нужно аккуратно остановить его при прерывании, завершении и т. Д. c. сигналы.

Здесь приведен фрагмент для иллюстрации соответствующих частей логики c.

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

Как вы можете видеть в signal_handler, может быть ситуация, когда даже running установлен на false, condition уведомляется, что все еще существует сценарий (описанный с помощью встроенных комментариев), когда поток переходит в спящий режим на 1 час. Это происходит потому, что вокруг переменной running нет мьютекса. Это позволяет потоку заблокировать мьютекс и проверить условие непосредственно перед тем, как переменная будет установлена, переменная установлена. Если я добавлю что-то вроде

  {
      std::lock_guard<std::mutex> lock(mutex);
      running = false;
  }

в обработчик, которого следует избегать.

Тогда возникает вопрос, как использовать (возможно ли вообще) мьютексы без получения потенциальной тупика или каких-либо других проблемы. Любые другие уловки, чтобы ослабить спящую нить от сигнала.

1 Ответ

2 голосов
/ 23 марта 2020

Надежный способ обработки сигналов в программе pthreads состоит в том, чтобы замаскировать все сигналы, которые вы sh обрабатываете в каждом потоке, и создать выделенный поток обработки сигналов, который зацикливается на вызове sigwaitinfo() (или sigtimedwait()) .

Поток обработки сигналов может затем использовать обычные общие переменные, защищенные мьютексом, и pthread_cond_signal() / pthread_cond_broadcast() пробуждения для информирования других потоков о полученных сигналах.

В вашем примере выделенный Поток обработки сигналов может безопасно заблокировать мьютекс перед изменением флага running, поскольку он находится в обычном контексте потока, а не в обработчике сигналов.

...