std::condition_variable 必须与 std::mutex 配合使用,因其自身无线程安全机制,依赖互斥锁保护条件检查、状态修改及唤醒后重检;wait() 需在锁持有时调用,且谓词必须用 while 循环防止虚假唤醒。

为什么 std::condition_variable 必须和 std::mutex 配合使用
因为 std::condition_variable 本身不提供线程安全,它只负责“通知”和“等待”,而等待前的条件检查、状态修改、唤醒后的重检,全靠互斥锁保护。漏掉锁,wait() 可能永远卡住,或出现虚假唤醒后读到脏数据。
- 必须在
lock持有时调用wait(),否则抛std::system_error(错误信息:operation not permitted) - 推荐用
std::unique_lock<:mutex></:mutex>,不是std::lock_guard—— 因为wait()会自动释放锁并挂起,唤醒时重新加锁,lock_guard不支持这种移交 - 条件判断一定要写在
while循环里,不能用if:防止虚假唤醒后直接往下执行,跳过实际检查
notify_one() 和 notify_all() 怎么选
选错会导致吞吐下降或死锁。核心看「唤醒后是否所有等待线程都能推进」。
- 生产者每次只放一个任务?用
notify_one()更轻量,避免唤醒多个消费者抢同一个空队列 - 消费者有多个且队列可能长期非空?仍优先
notify_one(),减少竞争 - 只有当「一个通知需触发多个逻辑分支」才考虑
notify_all(),比如:队列满时阻塞生产者 + 队列空时阻塞消费者,共用一个condition_variable(不推荐,应分设) - 滥用
notify_all()会让所有等待线程争抢锁、重检条件,CPU 白耗,尤其在线程数多时明显
队列为空/满时的等待逻辑怎么写才不出错
关键不是“等空”或“等满”,而是“等可操作”——即等待条件满足后能立刻执行下一步(push 或 pop),中间不能有竞态窗口。
- 生产者等待:用
while (queue.size() >= capacity),不是==—— 防止多个生产者同时被唤醒,一起发现队列已满 - 消费者等待:用
while (queue.empty()),不是if—— 唤醒后必须重新检查,因可能被其他消费者抢先取走 - 容量检查必须在持有锁的前提下做;但
push()/pop()本身也要在锁内完成,否则破坏原子性 - 示例片段:
std::unique_lock<:mutex> lock(mtx); cond_var.wait(lock, [&] { return !queue.empty(); }); auto item = std::move(queue.front()); queue.pop(); // lock 自动释放,item 已安全取出</:mutex>
为什么用 std::queue 而不用 std::vector 或 std::deque 直接当队列
std::queue 是适配器,默认底层是 std::deque,它比 std::vector 更适合高频头尾操作;但真正要注意的是「不要暴露底层容器」。
- 别直接访问
std::queue的c成员(如q.c.size()),这是未定义行为,标准不保证其存在 - 需要容量限制时,自己封装 size 计数器或用
std::deque手动管理,避免每次size()都遍历(某些实现下std::queue::size()是 O(n)) - 如果要用
std::vector模拟循环队列,务必自己处理索引越界和空满判别,比std::deque复杂得多,没优势 - 性能上,
std::deque的 push/pop front/back 平均 O(1),内存局部性略差于 vector,但对生产者消费者模型影响极小
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











