核心是用 std::mutex 保护共享缓冲区,配合 std::condition_variable 避免忙等和死锁;必须用 while 循环检查条件防虚假唤醒,所有队列操作需在 unique_lock 下执行,notify 用于线程间通知,超时等待用 wait_for/wait_until,调试需防 lock 状态异常和 cond_var 混用。

用 std::mutex + std::condition_variable 实现线程安全的队列
核心是保护共享缓冲区,避免生产者写入时消费者读取到中间状态。不能只靠 std::mutex 加锁,否则会忙等或死锁——必须配合 std::condition_variable 让空闲线程挂起等待条件满足。
典型错误是忘记在 wait() 的 lambda 中检查条件是否真的成立(虚假唤醒),或者没在循环里重检:
while (queue.empty()) {
cond_var.wait(lock); // 必须用 while,不能用 if
}
-
std::queue本身不线程安全,所有push()、pop()、empty()都得在同一个std::unique_lock<:mutex></:mutex>下执行 - 生产者调用
cond_var.notify_one()(或notify_all())通知至少一个等待的消费者;消费者同理通知生产者 - 如果缓冲区有容量限制,生产者需在
queue.size() 为真时才 push,否则也要 wait
为什么不用 std::semaphore(C++20)?
std::semaphore 看起来更贴近信号量语义,但实际使用中容易漏掉资源释放或误用 acquire()/release() 顺序。比如消费者先 acquire() 空闲槽位信号量再加锁,就可能和生产者形成锁序反转,引发死锁。
相比之下,std::condition_variable 强制你把“等待条件”和“持有锁”绑定在一起,天然规避了部分竞态场景。
立即学习“C++免费学习笔记(深入)”;
- C++20
std::semaphore在 Windows 上依赖CreateSemaphoreEx,某些旧 MSVC 工具链支持不完整 - 很多项目仍要求 C++17 兼容,
std::semaphore不可用 - 用
std::condition_variable写出来的逻辑更容易被 Code Review 发现条件判断漏洞
阻塞 vs 超时消费:怎么让消费者不永久卡住?
真实服务中,消费者不能无限期等一个永远不来的新任务。用 wait_for() 或 wait_until() 替代 wait() 即可:
auto timeout = std::chrono::seconds(5);
if (cond_var.wait_for(lock, timeout, [&]{ return !queue.empty(); })) {
// 成功等到,可以 pop
} else {
// 超时,做清理或心跳上报
}
- 注意:超时返回
false只表示“没等到条件成立”,不代表队列一定为空——可能刚被另一个线程消费完 - 若需精确控制最大等待时间,优先用
wait_until()配合单调时钟,避免系统时间跳变影响 - 不要在超时分支里直接退出线程,除非上层明确允许该消费者生命周期结束
调试时常见崩溃点在哪?
最常触发的是 std::condition_variable::wait() 抛 std::system_error,错误信息通常是 operation not permitted 或 invalid argument。
- 根本原因:传给
wait()的std::unique_lock没有真正持有锁(比如 lock 构造失败、已被 move 走、或已 unlock) - 另一个高频问题:多个
std::condition_variable共享同一个 mutex,但等待逻辑混用了不同变量(比如生产者 notify 了 A,消费者却在等 B) - 用 AddressSanitizer + ThreadSanitizer 编译运行,能快速定位未初始化的 condition_variable 或 double unlock
缓冲区大小设为 1 时特别容易暴露唤醒丢失问题——建议单元测试里强制跑单元素队列 + 高频生产/消费交叉,比默认容量更能暴露同步缺陷。


















