【发布时间】:2020-04-18 13:50:34
【问题描述】:
我试图理解std::condition_variable 的语义。我以为我对 C++11 并发模型(原子、内存排序、对应的guarantees and formal relations)有相当的了解,但是关于如何正确使用条件变量的描述似乎与我的理解相矛盾。
TL;DR
reference 说:
打算修改变量的线程必须
- 获取 std::mutex(通常通过 std::lock_guard)
- 在持有锁时执行修改
- 在 std::condition_variable 上执行 notify_one 或 notify_all(通知不需要持有锁)
即使共享变量是原子的,也必须在互斥体下进行修改,才能正确地将修改发布到等待线程。
我明白为什么修改可能必须在释放互斥锁之前完成,但上面似乎很清楚,它必须同时持有互斥锁,即它不能在获取之前。我读对了吗?
更详细
如果我对上述内容的理解是正确的,那么为什么会这样呢?考虑我们在关键部分之前进行修改(通过正确使用原子和锁来确保没有竞争条件)。例如
std::atomic<bool> dummy;
std::mutex mtx;
std::condition_variable cv;
void thread1() {
//...
// Modify some program data, possibly in many places, over a long period of time
dummy.store(true, std::memory_order_relaxed); // for simplicity
//...
mtx.lock(); mtx.unlock();
cv.notify_one();
//...
}
void thread2() {
// ...
{ std::unique_lock<std::mutex> ul(mtx);
cv.wait(ul, []() -> bool {
// A complex condition, possibly involving data from many places
return dummy.load(std::memory_order_relaxed); // for simplicity
});
}
// ...
}
我的理解是cv.wait() 在继续之前锁定mtx(检查条件并执行程序的其余部分)。此外,std::mutex::lock() 算作 acquire 操作,std::mutex::unlock() 算作 release 操作。这是否意味着 thread1 中的 unlock() 同步 thread2 中的 lock(),因此在 unlock() 之前在 thread1 中执行的所有原子甚至非原子存储对 thread2 都是可见的它醒来了吗?
Formally: store --sequenced-before--> unlock() --synchronizes-with--> lock() --sequenced-before--> load
...and so: store --happens-before--> load
非常感谢您的任何回答!
[注意:经过大量谷歌搜索后,我还没有找到答案,这很奇怪;如果是重复的,我很抱歉...]
【问题讨论】:
标签: c++ c++11 concurrency semantics condition-variable