You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 14:32:53