std::condition_variable必须搭配std::mutex使用,因其不保证线程安全,需互斥量保护共享状态以避免信号丢失;正确用法是用std::unique_lock配合带谓词的wait,并在同锁下notify。

std::condition_variable 为什么必须搭配 std::mutex 使用
因为 std::condition_variable 本身不提供线程安全的等待/通知逻辑,它只负责阻塞和唤醒,状态变更(比如“是否就绪”)必须由外部互斥量保护。直接裸用会导致竞态:一个线程刚检查完条件为假、还没调用 wait(),另一个线程就发了 notify_one(),信号丢失。
正确做法是把共享状态(如 ready_flag)和 wait() 放在同一个 std::unique_lock<:mutex></:mutex> 作用域内:
std::mutex mtx;
std::condition_variable cv;
bool ready_flag = false;
// 等待线程
{
std::unique_lock<:mutex> lk(mtx);
cv.wait(lk, []{ return ready_flag; }); // 自动释放锁 + 唤醒后重新加锁
}
</:mutex>
- 必须用
std::unique_lock(不能用std::lock_guard),因为wait()需要临时释放锁 - 使用带谓词的
wait(lk, pred)而非裸wait(lk),避免虚假唤醒 - 通知方修改
ready_flag后,必须在同一个mtx保护下调用cv.notify_one()或cv.notify_all()
std::promise/std::future 怎么用于一次性的异步结果通知
当你只需要“通知一次 + 附带一个值”(比如后台计算完成并返回结果),std::promise 比手动管理 condition_variable 更简洁、更难出错。
关键点:
-
std::promise对象通常在线程外创建,其get_future()返回的std::future可传入等待线程 - 后台线程调用
promise.set_value(val)(或set_exception())即触发通知,且只能调用一次 - 等待线程调用
future.get()会阻塞直到被设置,之后自动获取值或抛出异常
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread t([&p]{
std::this_thread::sleep_for(1s);
p.set_value(42); // 通知完成并传递结果
});
t.detach();
int result = f.get(); // 阻塞直到 set_value 被调用
</int></int>
注意:std::future 不可拷贝(只能移动),多次调用 get() 会抛出 std::future_error。
std::atomic 能否替代 condition_variable 实现简单通知
可以,但仅限极简场景:单次通知 + 无等待阻塞需求 + 允许忙等(spin-wait)。
例如两个线程严格配对、且通知延迟敏感(微秒级),可用 std::atomic<bool></bool> 配合 std::this_thread::yield() 或 pause 指令优化:
std::atomic<bool> notified{false};
// 通知方
notified.store(true, std::memory_order_release);
// 等待方(不推荐在高负载或长等待时用)
while (!notified.load(std::memory_order_acquire)) {
std::this_thread::yield(); // 让出 CPU 时间片
}
</bool>
- 没有阻塞,CPU 占用率高;无法传递数据;无法区分多次通知
- 内存序必须配对:通知用
store(..., release),等待用load(..., acquire) - 如果等待时间不可控,优先选
condition_variable或promise/future
多个等待者同时被唤醒时怎么避免惊群效应
notify_all() 会唤醒所有等待线程,但只有第一个抢到锁并满足条件的线程能真正继续,其余线程会重新进入等待——这就是惊群。解决思路不是禁用 notify_all(),而是让通知与具体条件绑定。
典型做法:
- 用多个独立的
std::condition_variable+ 多个标志位,每个等待者监听专属条件 - 或者用一个
condition_variable,但等待谓词中判断“自己关心的子条件”,例如:cv.wait(lk, [&]{ return queue.size() > 0 && is_my_task(queue.front()); }); - 避免在 notify 前广播式修改全局状态;尽量让通知精准对应某个等待者的预期
最容易被忽略的是:即使你只调用 notify_one(),若多个线程在等同一个条件变量,而该条件对多个线程都成立(比如队列里有多个任务),那后续仍需靠业务逻辑决定谁真正消费——这不属于同步机制问题,而是设计问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











