C++17 信號量模擬實現
一、實現原理
C++17 標準庫沒有原生信號量(C++20才有),但可以通過 std::mutex
+ std::condition_variable
模擬實現。以下是核心邏輯:
#include <mutex>
#include <condition_variable>class CountingSemaphore {
private:int count_; // 當前可用資源數std::mutex mutex_; // 保護計數器的鎖std::condition_variable cv_; // 阻塞/喚醒線程public:explicit CountingSemaphore(int initial = 0) : count_(initial) {}// P操作:請求資源(計數器減1)void acquire() {std::unique_lock<std::mutex> lock(mutex_);cv_.wait(lock, [this] { return count_ > 0; }); // 等待資源可用--count_;}// V操作:釋放資源(計數器加1)void release() {std::lock_guard<std::mutex> lock(mutex_);++count_;cv_.notify_one(); // 喚醒一個等待線程}
};
二、完整可編譯代碼
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>// 信號量實現類
class CountingSemaphore {
private:int count_;std::mutex mutex_;std::condition_variable cv_;
public:explicit CountingSemaphore(int initial = 0) : count_(initial) {}void acquire() {std::unique_lock<std::mutex> lock(mutex_);cv_.wait(lock, [this] { return count_ > 0; });--count_;}void release() {std::lock_guard<std::mutex> lock(mutex_);++count_;cv_.notify_one();}
};// 全局資源
constexpr int MAX_BUFFER = 5;
CountingSemaphore empty_slots(MAX_BUFFER); // 初始空位
CountingSemaphore data_items(0); // 初始數據項
std::mutex buffer_mutex; // 保護緩沖區
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(buffer_mutex);buffer.push(i);std::cout << "生產者添加: " << i << std::endl;}data_items.release(); // 增加數據項std::this_thread::sleep_for(std::chrono::milliseconds(100));}// 標記生產完成std::lock_guard<std::mutex> lock(buffer_mutex);producer_done = true;
}// 消費者函數
void consumer() {while (true) {data_items.acquire(); // 等待數據項{std::lock_guard<std::mutex> lock(buffer_mutex);if (producer_done && buffer.empty()) break; // 退出條件int val = buffer.front();buffer.pop();std::cout << "消費者取出: " << val << std::endl;}empty_slots.release(); // 釋放空位std::this_thread::sleep_for(std::chrono::milliseconds(150));}
}int main() {std::thread t1(producer);std::thread t2(consumer);t1.join();t2.join();std::cout << "程序正常退出" << std::endl;return 0;
}
三、編譯與運行
-
編譯命令(需要支持C++17的編譯器):
g++ -std=c++17 -pthread -o semaphore_demo semaphore_demo.cpp
-
運行輸出示例:
四、關鍵機制解析
組件 | 作用 |
---|---|
std::mutex | 保護共享計數器 count_ 的線程安全訪問 |
std::condition_variable | 實現阻塞等待和喚醒機制 |
cv_.wait() | 阻塞線程直到資源可用(count_ > 0 ) |
cv_.notify_one() | 資源釋放后喚醒一個等待線程 |
五、注意事項
-
虛假喚醒處理:
cv_.wait()
必須使用循環判斷條件cv_.wait(lock, [this] { return count_ > 0; }); // 正確寫法
-
死鎖避免:確保每次
acquire()
都有對應的release()
-
性能優化:高頻場景下可改用
notify_all()
+ 線程競爭void release() {std::lock_guard<std::mutex> lock(mutex_);++count_;cv_.notify_all(); // 喚醒所有線程 }
C語言的信號量
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem;
int shared_counter = 0;void* thread_func(void* arg) {for (int i = 0; i < 100000; i++) {sem_wait(&sem); // P操作shared_counter++; // 臨界區sem_post(&sem); // V操作}return NULL;
}int main() {// 初始化信號量(初始值為1)if (sem_init(&sem, 0, 1) != 0) {perror("sem_init failed");return 1;}pthread_t t1, t2;// 創建線程1if (pthread_create(&t1, NULL, thread_func, NULL) != 0) {perror("pthread_create t1 failed");return 1;}// 創建線程2if (pthread_create(&t2, NULL, thread_func, NULL) != 0) {perror("pthread_create t2 failed");return 1;}// 等待線程結束pthread_join(t1, NULL);pthread_join(t2, NULL);printf("Final counter value: %d\n", shared_counter); // 正確應為200000// 銷毀信號量sem_destroy(&sem);return 0;
}