C++ 20 信号量详解
一、信号量类型
C++20 标准中定义了两种信号量:
std::counting_semaphore<Max>
:计数信号量(允许资源池最多有Max
个资源)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;
}
三、编译与运行
-
编译命令(需要支持C++20的编译器):
g++ -std=c++20 -pthread -o semaphore_demo semaphore_demo.cpp
-
输出示例:
四、核心概念解析
-
acquire()
(P操作):- 减少信号量计数器
- 若计数器为0则阻塞,直到有其他线程执行
release()
-
release()
(V操作):- 增加信号量计数器
- 唤醒等待中的线程(如果有)
-
二进制信号量特性:
- 初始值设为1时等价于互斥锁
- 但释放操作可由任意线程执行(与互斥锁不同)
五、关键点总结
特性 | 计数信号量 | 二进制信号量 |
---|---|---|
最大计数值 | 模板参数指定(如<5> ) | 固定为1 |
典型应用场景 | 资源池管理 | 互斥访问/同步标志 |
线程唤醒策略 | 先进先出(FIFO) | 取决于具体实现 |
内存占用 | 每个实例约4-8字节 | 同计数信号量 |