关于互斥锁保护condition_variable及sleep_for实现的技术问询
ext::sleep_for()实现中互斥锁与条件变量的疑问解答 首先贴出你修改后的代码实现:
#include <chrono> /// milliseconds #include <mutex> /// mutex, unique_lock #include <condition_variable> /// condition_variable /// declarations ... namespace ext { void sleep_for(int ms); } /// implementation ... void ext::sleep_for(int ms) { std::mutex mtx; std::condition_variable timercv; /// acquire mtx std::unique_lock<std::mutex> lck {mtx}; /// release and reacquire mutex timercv.wait_for(lck, std::chrono::milliseconds {ms}); } // implicitly release mtx
Stroustrup的原文说明:
互斥锁可防止wait_for()出现数据竞争。wait_for()在进入休眠时释放互斥锁,在线程被唤醒时重新获取互斥锁。
接下来逐个解答你的疑问:
疑问1解答
其实Stroustrup的表述容易让人产生误解,他说的"保护wait_for()避免数据竞争",本质是因为C++标准对condition_variable::wait_for()有强制要求:调用该函数时必须持有关联的互斥锁。如果违反这个要求,会触发未定义行为(比如线程调度混乱、内部状态竞态等)。这里的"数据竞争"不是指条件变量本身的并发访问,而是指不遵循wait操作的锁要求会导致的未定义竞态情况。
疑问2解答
你观察得非常准确:这里的timercv是函数局部变量,每个调用ext::sleep_for()的线程都会创建完全独立的副本,根本不存在多个线程并发访问同一个condition_variable实例的场景。那为什么还要用互斥锁?答案很直接:这是标准库的硬性规定——condition_variable的所有wait系列函数(包括wait_for()),都要求调用者必须持有一个已锁定的unique_lock<std::mutex>,否则行为未定义。这和有没有全局共享数据无关,哪怕你没有要保护的共享状态,只要用wait_for,就必须满足这个前提。
疑问3解答
这个互斥锁完全不是用来保护condition_variable实例的(毕竟每个线程都有自己的副本,没有并发访问的可能)。它的唯一作用就是满足wait_for()的调用规范:wait_for内部会先自动释放锁,让线程进入休眠;当超时时间到达(这里没有其他线程调用notify,所以只会触发超时),它会重新尝试获取锁(因为mutex是局部的,没有其他线程竞争,所以会立刻拿到),然后函数返回,最后unique_lock的析构函数自动释放锁。整个过程中,互斥锁只是标准库要求的"配套组件",没有保护任何共享数据。
关于你的理解补充
你的两点理解方向是对的,我再补充一点细节让它更完整:
- 需要mutex并获取锁,完全是为了符合
wait_for()的调用前置条件,这是标准强制要求的; condition_variable::wait_for()确实会自动释放互斥锁让线程休眠,超时后重新获取锁(这里无竞争,所以瞬间拿到),函数返回后锁会被unique_lock自动释放。
内容的提问来源于stack exchange,提问作者SSteven

