为什么 C++20 std::condition_variable 不支持 std::stop_token?

时间:2021-02-22 02:24:42

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

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

1 个答案:

答案 0 :(得分:3)

是的,存在竞争条件。

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

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

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是可能的,我将不得不做很多艰苦的证明工作。