C++生产者-消费者线程安全协作有三种方案:一、基础版用std::queue+mutex+condition_variable,wait需while循环防虚假唤醒;二、优化版用shared_mutex分离读写锁,状态检查用共享锁,修改用独占锁;三、C++20版用counting_semaphore替代条件变量,以empty_slots和filled_items信号量控制流程。

如果您在 C++ 中尝试实现生产者与消费者线程安全协作,但发现数据竞争、死锁或唤醒丢失问题,则可能是由于 std::condition_variable 的等待条件未与互斥锁严格配合 或 通知时机与谓词状态不一致。以下是三种可独立运行的实现方案:
一、基于 std::queue + std::mutex + std::condition_variable 的基础版本
该方案使用标准容器和原生同步原语,核心在于确保每次 wait() 前检查谓词,并在 notify_one() 前修改共享状态。所有对队列的访问必须被同一互斥量保护,且 wait() 必须置于 while 循环中防止虚假唤醒。
1、定义线程安全队列类,包含私有成员 std::queue
2、在 produce() 方法中,获取 std::unique_lock<:mutex> 锁,while (buffer.size() == capacity) 调用 cv_not_full.wait(lock)。
3、在 buffer.push(value) 后,调用 cv_not_empty.notify_one()。
4、在 consume() 方法中,同样获取 unique_lock,while (buffer.empty()) 调用 cv_not_empty.wait(lock)。
5、执行 value = buffer.front(); buffer.pop(); 后,调用 cv_not_full.notify_one()。
二、使用 std::shared_mutex 实现读写分离优化版本
当消费者数量远多于生产者且读操作频繁时,可将缓冲区状态检查(如 empty()、size())与实际取值/存值解耦,利用 std::shared_mutex 允许多个消费者并发检查状态,仅在真正修改队列时升级为独占锁。
1、将 std::mutex 替换为 std::shared_mutex,声明为 mutable 以支持 const 成员函数中加锁。
2、在 is_empty() 和 size() 方法中使用 std::shared_lock<:shared_mutex> 获取共享锁。
3、在 push() 和 pop() 中使用 std::unique_lock<:shared_mutex> 获取独占锁,并在修改后分别 notify_one() 对应的 condition_variable。
4、保持 wait() 的谓词循环不变,但等待前的状态检查可使用共享锁快速完成。
5、注意:std::condition_variable 不兼容 shared_mutex,因此仍需一个 std::mutex 专用于 cv 的 wait() 操作,与 shared_mutex 分离使用。
三、基于 std::semaphore(C++20)与 std::counting_semaphore 的无条件变量版本
该方案规避了 condition_variable 对谓词和虚假唤醒的手动处理,直接使用计数信号量控制资源可用性,每个信号量对应一种资源状态:empty_slots 控制剩余容量,filled_items 控制已填充项数。
1、声明两个 std::counting_semaphore
2、在 produce() 中,先调用 empty_slots.acquire(),再执行 buffer.push(),最后调用 filled_items.release()。
3、在 consume() 中,先调用 filled_items.acquire(),再执行 buffer.front() 和 pop(),最后调用 empty_slots.release()。
4、移除所有 std::mutex 和 std::condition_variable 相关代码,buffer 访问仍需原子操作或额外互斥——但因 push/pop 是临界区且由信号量顺序化,可改用 std::atomic
5、若使用 std::queue,则仍需 std::mutex 保护其内部状态,此时信号量仅作流程控制,互斥锁仅用于 queue 操作本身。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











