std::condition_variable::wait()如何评估给定的谓词

How does std::condition_variable::wait() evaluate the given predicate?

本文关键字:评估 谓词 何评估 wait variable condition std      更新时间:2023-10-16

上下文:

在我看到的关于std::condition_variable::wait()使用的每一个例子中,包括来自cppreference.com的例子,从来没有任何同步机制用于保护谓词求值不受数据竞争的影响。

例如:

std::mutex m;
std::condition_variable cv;
int i = 0;
void waiting_func()
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [](){return i > 0;}); // No lock/unlock around the access of the global and shared variable i.
// ...
}

问题:

如果没有这样的同步,即使是来自信誉良好的来源的例子,我想这是因为没有必要
但我想知道为什么?std::condition_variable::wait()如何评估谓词以使其线程安全?

我的想法:

我提出了两种可能性:

  1. (或者,谓词保证是原子求值的(我从来没有读过这样的东西,因此我提出了问题(
  2. )或者,当发送通知信号时,std::condition_variable::wait()函数在评估谓词之前重新获取互斥

在点2.(的情况下,如果修改i(并调用std::condition_variable::notify_one()(的线程在这样做之前锁定互斥体m,则可能是安全的

例如:

void modify_func()
{
{
std::scoped_lock<std::mutex> lk(m); // Acquire the mutex
i += 1;                             // Modify i
}                                       // Release the mutex
cv.notify_one();
}

当然,另一种可能性是我的理解完全错误,当时我没有抓住要点。

不管怎样,我真的很惊讶,我在文档中找不到任何关于它的细节。

您的第二种选择是正确的。正如cppreference上所描述的,谓词重载的行为与一样

while (!pred()) {
wait(lock);
}

并且wait(lock)总是在返回之前重新定位。参见C++17标准(草案N4659(的[thred.condition.convar]/12中的wait后条件和[thread.convation.convar]/15中的谓词重载行为。

是的,在修改i时,互斥体必须被锁定,即使它是原子的。例如,请参见此问题。