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

Why does C++20 std::condition_variable not support std::stop_token?

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

P0660R10 Stop Token and Joining Thread, Rev 10 说:

New in R6

  • User condition_variable_any instead of consition_variable to avoid all possible races, deadlocks, and unintended undefined behavior.

我认为以下代码安全地模拟了 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();
}

是的,存在竞争条件。

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

将您的状态设为原子变量并不能避免该问题。

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