![](/img/trans.png)
[英]How std::condition_variable and std::mutex works exactly?
[英]Mutex protecting std::condition_variable
即使共享变量是原子的,也必须在互斥锁下进行修改,才能将修改正确发布到等待线程。 任何打算等待 std::condition_variable 的线程都必须在用于保护共享变量的同一互斥锁上获取 std::unique_lock
我明白,通过使用互斥锁保护 std::condition_variable,我们可以防止在等待线程实际上没有等待时丢失通知。 这里已经回答: 如果没有在互斥锁下修改共享原子变量,则不会正确发布
我想知道的是,是否可以仅使用互斥锁来保护 std::condition_variable 以及对共享数据的某种其他形式的保护? 如果我们修改另一个答案中给出的示例,这会起作用吗?
std::atomic_bool proceed(false);
std::mutex m;
std::condition_variable cv;
std::thread t([&m,&cv,&proceed]()
{
{
std::unique_lock<std::mutex> l(m);
while(!proceed) {
hardWork();
cv.wait(l);
}
}
});
proceed = true;
{
std::lock_guard<std::mutex> lock(m);
}
cv.notify_one();
t.join();
或者我错过了内存排序或缓存有什么问题吗?
更新
我知道互斥锁通常也保护共享数据,使用原子变量只是一个例子。 问题不在于如何保护共享数据,而在于是否有必要使用相同的互斥锁来保护两者。 另一个使用第二个互斥锁的例子:
bool proceed(false);
std::mutex boolMutex;
std::mutex cvMutex;
std::condition_variable cv;
std::unique_lock<std::mutex> l(cvMutex);
void setBool()
{
std::lock_guard<std::mutex> lock(boolMutex);
proceed = true;
}
bool checkBool()
{
std::lock_guard<std::mutex> lock(boolMutex);
return proceed;
}
void worker()
{
while (true)
{
cv.wait(l);
if (checkBool()) {
// Do work
return;
}
}
}
int main()
{
std::thread t(worker);
setBool();
{
std::lock_guard<std::mutex> lock(cvMutex);
}
cv.notify_one();
t.join();
return 0;
}
必须设置 mutex-protected 标志,并通知条件变量,而 mutex 仍被保留:
{
std::lock_guard<std::mutex> lock(m);
proceed = true;
cv.notify_one();
}
此外,在这种情况下, proceed
标志不需要是原子实体。 一个简单的
bool proceed;
就足够了。 只有在持有相关联的互斥锁时才能访问proceed
,因此使proceed
原子化绝对不会完成任何事情。
atomic
实体用于处理不涉及任何互斥锁的奇异并发情况。
我不认为山姆的回答是正确的。 考虑以下代码:
// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
// thread #2:
proceed = true; // atomic to avoid data race
cv.notify_one();
这里的问题是以下可能的事件序列:
thread #1: while (!proceed) // evaluated as true
thread #2: proceed = true;
thread #2: cv.notify_one();
thread #1: cv.wait(l); // never gets notified
为了避免这种情况,一个典型的解决方案是使用相同的互斥锁保护proceed
修改:
// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
// thread #2:
{
std::lock_guard<std::mutex> l(m);
proceed = true; // does not need to be atomic
}
cv.notify_one();
现在, proceed = true;
必须发生在while (!proceed)
或之后cv.wait(l);
开始等待; 都可以。 在第一种情况下,根本不需要等待; 在第二种情况下, cv.notify_one();
保证仅在cv.wait(l);
时发生cv.wait(l);
实际上是在等待。
现在,你的(学术类)案例呢?
// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
// thread #2:
proceed = true; // atomic to avoid data race
{
std::lock_guard<std::mutex> lock(m);
}
cv.notify_one();
我相信这种情况也是完全有效的,因为上述错误情况也不会发生。 原因很简单。 如果while (!proceed)
被评估为 false,同样,没有等待。 并且,如果while (!proceed)
被评估为真,则通知不会发生,直到cw.wait(l);
被调用。
结论
我相信你的第一个例子没问题,cppreference 的引用是不正确的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.