std::condition_variable 必须与 std::unique_lock 配合使用,因其 wait() 内部需自动解锁/重锁;应优先使用带谓词的 wait(cv, []{return pred;}) 避免虚假唤醒;notify_one() 通常优于 notify_all() 以避免惊群效应。

condition_variable 必须和 unique_lock 配合使用
直接用 std::mutex 加锁后调用 wait() 会编译失败,因为 std::condition_variable::wait 只接受 std::unique_lock<:mutex></:mutex>。这是强制设计,不是可选项。
原因在于:等待前必须自动释放锁,唤醒后必须自动重获锁——unique_lock 支持移动、延迟锁定、手动释放(unlock())等能力,而 lock_guard 不支持。
-
wait()内部会先调用unlock(),再挂起线程;被唤醒时自动调用lock() - 若传入
lock_guard,编译器报错:no matching function for call to 'wait' - 别试图用
shared_lock或裸指针绕过——类型不匹配,无法通过编译
wait() 的谓词版本比无参版更安全
写成 cv.wait(lock, []{ return ready; }); 而不是 cv.wait(lock);,能避免虚假唤醒(spurious wakeup)导致的逻辑错误。
无参 wait() 返回后,条件未必成立;而带谓词的版本会自动循环检查,只在条件为 true 时才退出。
立即学习“C++免费学习笔记(深入)”;
- 虚假唤醒是 POSIX 和 C++ 标准允许的行为,不依赖系统实现,必须处理
- 手写 while 循环 + 无参 wait 也能工作,但容易漏掉
lock.unlock()或重复判断 - 谓词内部访问的变量(如
ready)必须在 lambda 中按引用捕获,且保证生命周期长于等待过程
notify_one() 和 notify_all() 的选择影响性能与正确性
多数场景下优先用 notify_one();只有当多个等待线程都需响应同一事件时,才用 notify_all()。
误用 notify_all() 会导致“惊群效应”:所有等待线程被唤醒,但只有一个能真正推进逻辑,其余再次进入等待——浪费调度开销,还可能引发竞争。
-
notify_one()唤醒**至少一个**等待线程(具体哪个由实现决定,不可预测) -
notify_all()唤醒**所有**当前在wait()的线程 - 生产者-消费者模型中,单个产品入队通常只需唤醒一个消费者,用
notify_one() - 若用
notify_one()却有多个消费者在等,可能造成部分消费者永久阻塞(除非配合超时或重试机制)
完整示例:生产者通知一个消费者获取数据
下面是一个最小可行实例,展示如何用 std::condition_variable 实现线程间信号传递:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
int data = 0;
void consumer() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 谓词确保非虚假唤醒
std::cout << "Consumed: " << data << "\n";
ready = false;
}
void producer() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
{
std::lock_guard<std::mutex> lock(mtx);
data = 42;
ready = true;
}
cv.notify_one(); // 只唤醒一个等待者
}
int main() {
std::thread t1(consumer);
std::thread t2(producer);
t1.join();
t2.join();
}
注意 notify_one() 必须在修改共享状态(ready = true)并解锁之后调用,否则消费者可能在状态更新前就被唤醒,导致读到旧值。
真正难调试的点往往不在语法,而在“谁在什么时候改了什么变量、锁是否覆盖了全部临界区、notify 是否发生在 wait 之前”——这些靠加日志不如靠严格分段加锁+谓词等待来规避。











