即使共享变量是原子的,也必须在 互斥为了正确地将修改发布到等待中 线。任何打算在std :: condition_variable上等待的线程都有 在与使用的互斥锁相同的互斥锁上获取std :: unique_lock 保护共享变量
据我所知,通过使用互斥锁保护std :: condition_variable,如果等待的线程实际上没有等待,我们可以防止丢失通知。这里已回答: Shared atomic variable is not properly published if it is not modified under mutex
我想知道的是,是否可以仅使用互斥锁来保护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;
}
答案 0 :(得分:0)
必须设置互斥锁保护标志,并且当互斥锁仍然被保持时,条件变量会发出信号:
{
std::lock_guard<std::mutex> lock(m);
proceed = true;
cv.notify_one();
}
此外,在这种情况下,proceed
标志不需要是原子实体。一个简单的
bool proceed;
就足够了。只有在持有关联的互斥锁时才能访问proceed
,使proceed
atomic完全没有任何结果。
atomic
个实体用于处理首先不涉及任何互斥锁的异常并发情况。
答案 1 :(得分:0)
我认为山姆的答案不正确。考虑以下代码:
// 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);
实际上正在等待时发生。
现在,您的(学术类)案件怎么样?
// 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)
评估为true,则在调用cw.wait(l);
之前不会发生通知 。
结论
我相信您的第一个示例还可以,并且cppreference中的引用不正确。