请考虑以下来自cppreference的关于std::condition_variable
的引用:
condition_variable
类是一个同步原语,可用于同时阻止一个或多个线程,直到另一个线程都修改了一个共享变量( condition ),并通知condition_variable
。打算修改变量的线程必须
- 获取
std::mutex
(通常通过std::lock_guard
)- 在锁定状态下执行修改
- 在
notify_one
上执行notify_all
或std::condition_variable
(不需要为通知而持有锁)即使共享变量是原子变量,也必须在互斥锁下对其进行修改,以便将修改正确发布到等待的线程中。
此方法的示例性典型方案如下:
// shared (e.d.,global) variables:
bool proceed = false;
std::mutex m;
std::condition_variable cv;
// thread #1:
{
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l); // or,cv.wait(l,[]{ return proceed; });
}
// thread #2:
{
std::lock_guard<std::mutex> l(m);
proceed = true;
}
cv.notify_one();
但是,this question中的@Tim提出了(一种学术上的)替代方案:
std::atomic<bool> proceed {false}; // atomic to avoid data race
std::mutex m;
std::condition_variable cv;
// thread #1:
{
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
}
// thread #2:
proceed = true; // not protected with mutex
{ std::lock_guard<std::mutex> l(m); }
cv.notify_one();
由于proceed
共享变量(在这种情况下是原子的)未在互斥锁下进行修改,因此显然不满足上述cppreference引用的要求。
问题是该代码是否正确。我认为是因为,
-
第一个选项是
!proceed
中的while (!proceed)
被评估为 false 。在这种情况下,根本不会调用cv.wait(l);
。 -
第二个选项是将
!proceed
中的while (!proceed)
评估为 true 。在这种情况下,cv.notify_one()
不会发生,直到线程#1进入cv.wait(l);
。
我在这方面是否缺少某些东西或cppreference是否错误?(例如,是否涉及一些重新排序问题?)
然后,如果将proceed = true;
更改为proceed.store(true,std::memory_order_relaxed);
怎么办?