基于锁和条件变量
基于信号量的生产者消费者模型是一种经典的同步机制,用于解决多线程或多进程之间的生产者消费者问题。在 C++ 中,可以使用 std::thread 和 std::mutex 实现基于信号量的生产者消费者模型。下面是一个简单的示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> q;
const int MAX_SIZE = 10; // 缓冲区最大容量
void producer() {
int i = 0;
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return q.size() < MAX_SIZE; }); // 等待缓冲区不满
q.push(i++);
std::cout << "Produced: " << i << std::endl;
lock.unlock();
cv.notify_all(); // 通知消费者可以消费
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 生产间隔 1 秒
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !q.empty(); }); // 等待缓冲区不空
int val = q.front();
q.pop();
std::cout << "Consumed: " << val << std::endl;
lock.unlock();
cv.notify_all(); // 通知生产者可以生产
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 消费间隔 1 秒
}
}
int main() {
std::thread producer_thread(producer);
std::thread consumer_thread(consumer);
producer_thread.join();
consumer_thread.join();
return 0;
}
在上述代码中,使用std::mutex和std::condition_variable实现同步和互斥操作,使用std::queue实现缓冲区,使用std::thread实现多线程。具体来说,生产者线程执行producer()函数,首先加锁,然后使用std::condition_variable的wait()方法等待缓冲区不满,当缓冲区不满时,生产者向缓冲区中添加数据,然后解锁并通知消费者线程可以消费。消费者线程执行consumer()函数,首先加锁,然后使用std::condition_variable的wait()方法等待缓冲区不空,当缓冲区不空时,消费者从缓冲区中取出数据,然后解锁并通知生产者线程可以生产。
需要注意的是,在使用std::condition_variable进行同步时,需要使用std::unique_lock或者std::lock_guard类来加锁和解锁,以确保线程安全性和正确性。
基于信号量
使用信号量实现生产者消费者模型的基本原理是:
- 定义两个信号量:一个用于表示缓冲区中可用的空间数量,一个用于表示缓冲区中已有的数据数量。
- 生产者线程在生产数据时,需要获取空间信号量的锁,如果空间信号量的值为 0,则等待消费者线程消费数据,直到有足够的空间可以生产数据。当生产者生产数据后,需要释放空间信号量的锁,并将数据数量信号量的值加 1。
- 消费者线程在消费数据时,需要获取数据数量信号量的锁,如果数据数量信号量的值为 0,则等待生产者线程生产数据,直到有足够的数据可以消费。当消费者消费数据后,需要释放数据数量信号量的锁,并将空间数量信号量的值加 1。
具体实现代码如下:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <semaphore.h>
sem_t empty_sem; // 空闲空间信号量
sem_t full_sem; // 已有数据信号量
std::mutex mtx;
std::queue<int> q;
const int MAX_SIZE = 10; // 缓冲区最大容量
void producer() {
int i = 0;
while (true) {
sem_wait(&empty_sem); // 等待空闲空间
std::unique_lock<std::mutex> lock(mtx);
q.push(i++);
std::cout << "Produced: " << i << std::endl;
lock.unlock();
sem_post(&full_sem); // 释放已有数据
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 生产间隔 1 秒
}
}
void consumer() {
while (true) {
sem_wait(&full_sem); // 等待已有数据
std::unique_lock<std::mutex> lock(mtx);
int val = q.front();
q.pop();
std::cout << "Consumed: " << val << std::endl;
lock.unlock();
sem_post(&empty_sem); // 释放空闲空间
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 消费间隔 1 秒
}
}
int main() {
sem_init(&empty_sem, 0, MAX_SIZE); // 初始化空闲空间信号量
sem_init(&full_sem, 0, 0); // 初始化已有数据信号量
std::thread producer_thread(producer);
std::thread consumer_thread(consumer);
producer_thread.join();
consumer_thread.join();
sem_destroy(&empty_sem); // 销毁空闲空间信号量
sem_destroy(&full_sem); // 销毁已有数据信号量
return 0;
}
在上述代码中,使用sem_t类型的empty_sem和full_sem表示空闲空间和已有数据的信号量,使用sem_wait()和sem_post()方法进行信号量的等待和释放操作。生产者线程和消费者线程分别使用sem_wait()和sem_post()方法等待和释放信号量,以实现缓冲区的同步和互斥操作。需要注意的是,使用信号量实现生产者消费者模型时,需要注意信号量的初始化和销毁操作,以避免资源泄漏和其他问题。同时,在使用sem_wait()方法等待信号量时,需要注意信号量的值是否为 0,以避免线程阻塞和死锁问题。
如果觉得不错,请留下你的点赞和收藏~