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字節 | 同計數信號量 |