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;
}