C++ 20 信号量详解

发布于:2025-04-21 ⋅ 阅读:(39) ⋅ 点赞:(0)

C++ 20 信号量详解

一、信号量类型

C++20 标准中定义了两种信号量:

  1. std::counting_semaphore<Max>:计数信号量(允许资源池最多有 Max 个资源)
  2. std::binary_semaphore:二进制信号量(等价于 std::counting_semaphore<1>

二、代码实现与详解
1. 计数信号量(生产者-消费者模型)
#include <iostream>
#include <thread>
#include <semaphore>
#include <queue>
#include <mutex>

// 最大缓冲区大小
constexpr size_t BUFFER_SIZE = 5;

// 定义信号量(空位初始为5,数据初始为0)
std::counting_semaphore<BUFFER_SIZE> empty_slots(BUFFER_SIZE);
std::counting_semaphore<BUFFER_SIZE> data_items(0);

std::mutex mtx;              // 保护共享队列的互斥锁
std::queue<int> buffer;       // 共享缓冲区
bool producer_done = false;  // 生产完成标志

void producer() {
    for (int i = 1; i <= 10; ++i) {
        empty_slots.acquire(); // 等待空位
        
        {
            std::lock_guard<std::mutex> lock(mtx);
            buffer.push(i);
            std::cout << "Product: " << i << std::endl;
        }
        
        data_items.release(); // 增加数据项
    }
    
    // 生产完成后设置标志
    std::lock_guard<std::mutex> lock(mtx);
    producer_done = true;
}

void consumer() {
    while (true) {
        data_items.acquire(); // 等待数据
        
        {
            std::lock_guard<std::mutex> lock(mtx);
            
            // 检查是否所有数据已消费
            if (producer_done && buffer.empty()) break;
            
            int val = buffer.front();
            buffer.pop();
            std::cout << "Consume: " << val << std::endl;
        }
        
        empty_slots.release(); // 释放空位
    }
}

int main() {
    std::jthread prod(producer); // C++20 自动管理线程
    std::jthread cons(consumer);
    
    return 0;
}

2. 二进制信号量(互斥访问)
#include <iostream>
#include <thread>
#include <semaphore>

std::binary_semaphore resource(1);  // 初始可用
int counter = 0;

void worker(int id) {
    for (int i = 0; i < 3; ++i) {
        resource.acquire();  // P操作
        ++counter;
        std::cout << "线程" << id << "修改计数器: " << counter << std::endl;
        resource.release();  // V操作
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    std::jthread t1(worker, 1);
    std::thread t2(worker, 2);
    
    t1.join();
    t2.join();
    return 0;
}

三、编译与运行
  1. 编译命令(需要支持C++20的编译器):

    g++ -std=c++20 -pthread -o semaphore_demo semaphore_demo.cpp
    
  2. 输出示例
    在这里插入图片描述


四、核心概念解析
  1. acquire()(P操作):

    • 减少信号量计数器
    • 若计数器为0则阻塞,直到有其他线程执行release()
  2. release()(V操作):

    • 增加信号量计数器
    • 唤醒等待中的线程(如果有)
  3. 二进制信号量特性

    • 初始值设为1时等价于互斥锁
    • 但释放操作可由任意线程执行(与互斥锁不同)

五、关键点总结
特性 计数信号量 二进制信号量
最大计数值 模板参数指定(如<5> 固定为1
典型应用场景 资源池管理 互斥访问/同步标志
线程唤醒策略 先进先出(FIFO) 取决于具体实现
内存占用 每个实例约4-8字节 同计数信号量

网站公告

今日签到

点亮在社区的每一天
去签到