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(); // 唤醒所有线程 }