【问题标题】:Why does C++20 std::condition_variable not support std::stop_token?为什么 C++20 std::condition_variable 不支持 std::stop_token?
【发布时间】:2021-05-24 07:35:15
【问题描述】:

在 C++20 标准库中,std::condition_variable_any::wait() 系列支持 std::stop_token 用于通用线程取消,但 std::condition_variable 不支持。

P0660R10 Stop Token and Joining Thread, Rev 10 说:

R6 中的新功能

  • 使用 condition_variable_any 而不是 consition_variable 以避免所有可能的竞争、死锁和意外的未定义行为。

我认为以下代码可以安全地模拟 condition_variable::wait() 并取消 stop_token。我错过了什么?是否存在微妙的边缘情况?

template<class Lock, class Predicate>
bool cv_wait_with_stoken(
  std::condition_variable& cv, Lock& lock, std::stop_token stoken, Predicate pred)
{
  std::stop_callback callback{ stoken, [&cv]{ cv.notify_all(); } };
  while (!stoken.stop_requested()) {
    if (pred())
      return true;
    cv.wait(lock);
  }
  return pred();
}

【问题讨论】:

    标签: c++ multithreading thread-safety c++20


    【解决方案1】:

    是的,有一个竞争条件。

    通常对于条件变量,您必须在修改受保护状态(通常是变量)和发出条件变量信号之间保持互斥锁一段时间。否则,您可能会错过信号。

    让你的状态成为一个原子变量并不能避免这个问题。

    cv 的等待代码首先检查状态。如果失败,它会原子地释放锁并等待信号。

    如果您的停止标记在检查后但在等待之前设置在该间隙中,则停止标记调用通知全部,该通知全部不会被条件变量拾取。

    cv.notify_all() 必须在获得该锁之前。这会打开一整罐蠕虫。

    不要使用此代码,它可能会由于双重锁定或无数其他事情而严重崩溃,但理论上它看起来像:

    bool cv_wait_with_stoken(
      std::condition_variable& cv,
      Lock& lock,
      std::stop_token stoken,
      Predicate pred
    ) {
      std::stop_callback callback{ stoken, [&cv]{
        lock.lock();
        lock.unlock();
        cv.notify_all();
      } };
      while (!stoken.stop_requested()) {
        if (pred())
          return true;
        cv.wait(lock);
      }
      return pred();
    }
    

    那里可以try_lock,我得做很多艰苦的证明工作。

    【讨论】:

    • 你打算写 lock.lock() -> cv.notify_all() -> lock.unlock() 在 stop_callback lambda 中排序吗?无论如何std::condition_variable 只接受std::unique_lock&lt;std::mutex&gt; 作为Lockable,所以如果线程调用request_stop() 持有互斥锁的锁,这样的stop_callback 可能会导致死锁。
    • @yohjp 我让蠕虫罐警告更加明确。锁只需要在状态改变和通知之间的某个时间点被持有;通常你也会保护变量。但是锁定/解锁足以避免竞争,因为等待代码在检查状态时持有锁定,然后自动解锁并等待信号。
    猜你喜欢
    • 2012-10-17
    • 2016-09-07
    • 1970-01-01
    • 2020-12-22
    • 1970-01-01
    • 1970-01-01
    • 2017-11-22
    • 1970-01-01
    相关资源
    最近更新 更多