基于C++20信号量的生产者消费者问题:是否仍需std::lock_guard?
生产者消费者问题:信号量与互斥锁的作用区别
问题
以下是基于C++20信号量实现的生产者消费者问题代码,我认为信号量已经具备互斥排他能力,为什么还需要使用std::lock_guard<std::mutex>?
#include <thread> #include <mutex> #include <semaphore> #include <queue> // 补充原代码遗漏的头文件 constexpr int N = 5; // 补充原代码未定义的队列容量常量 std::queue<int> q; std::counting_semaphore<N> full{0}; std::counting_semaphore<N> empty{N}; std::mutex mtx; void produce() { for (;;) { int item = 0; empty.acquire(); { std::lock_guard<std::mutex> g(mtx); q.push(item); } full.release(); } } void consume() { for (;;) { full.acquire(); { std::lock_guard<std::mutex> g(mtx); int item = q.front(); q.pop(); } empty.release(); } } int main() { std::thread t1(produce); // 修正原代码函数名拼写错误 std::thread t2(consume); t1.join(); t2.join(); }
为什么必须保留std::lock_guard?
你混淆了信号量的同步作用和互斥锁的互斥作用,两者职责完全不同:
信号管的是「什么时候能操作」,不是「谁能操作」
这里的empty和full信号量仅用来协调生产者和消费者的执行顺序:empty.acquire()确保生产者只在队列有空位时入队,full.acquire()确保消费者只在队列有元素时出队。但它无法阻止多个生产者(或多个消费者)同时通过信号量检查,进而同时操作队列。比如两个生产者线程可能同时通过empty.acquire(),然后并行执行队列写入操作。std::queue不是线程安全的
队列的push、front、pop等操作都不是原子操作,内部涉及修改节点指针、更新队列长度、内存分配等多步操作。如果多个线程同时操作同一个队列,会直接破坏队列的内部数据结构,引发数据竞争、程序崩溃或逻辑错误。互斥锁负责保护共享资源的互斥访问
std::lock_guard包裹的代码段,能保证同一时间只有一个线程可以执行队列的读写操作,彻底避免了多线程并行操作队列带来的风险。
简单来说:信号量解决「操作时机」的同步问题,互斥锁解决「资源访问」的互斥问题,两者缺一不可,不能互相替代。
内容的提问来源于stack exchange,提问作者Dengzhi Zhang
相关产品推荐
相关产品推荐

