一、模型简介:
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。
- 生产者与消费者的关系:
生产者——生产者 : 互斥;
消费者——消费者 : 互斥;
生产者——消费者 :同步与互斥。
二、模型实现分析:
二者是不同的不同角色的执行流;只需要执行中间实现线程安全的队列,再通过各自创建不同的角色的执行流就可以实现这个模型。
线程安全阻塞队列:stl中的队列是非线性安全的。
三、生产者与消费者模型实现:
1. 互斥锁与条件变量的方式实现:
①:阻塞队列的实现:
#include <cstdio>
#include <iostream>
#include <queue>
#include <pthread.h>
#define MAX_QUEUE 5
class BlockQueue
{
public:
BlockQueue(int max = MAX_QUEUE):_capacity(max){
pthread_mutex_init(&_mutex, NULL);
pthread_cond_init(&_cond_con, NULL);
pthread_cond_init(&_cond_pro, NULL);
}
~BlockQueue(){
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond_con);
pthread_cond_destroy(&_cond_pro);
}
bool Push(int data) {
//入队的操作,肯定是生产者
//1. 加锁操作
pthread_mutex_lock(&_mutex);
//2. 判断队列是否满了
while (_queue.size() == _capacity) {
//若不满足(队列已经满了),则线程陷入阻塞
pthread_cond_wait(&_cond_pro, &_mutex);
}
//3. 向队列中添加一个节点
_queue.push(data);
//4. 唤醒消费者
pthread_cond_signal(&_cond_con);
//5. 解锁操作
pthread_mutex_unlock(&_mutex);
return true;
}
// const & 表示输入参数
// * 表示输出参数
// & 表示输入输出参数
bool Pop(int *data) {
//出队的操作,肯定是消费者
//1. 加锁操作
pthread_mutex_lock(&_mutex);
//2. 判断队列是否为空
while (_queue.empty()) {
//若不满足(队列为空),则线程陷入阻塞
pthread_cond_wait(&_cond_con, &_mutex);
}
//3. 从队列中获取一个节点
*data = _queue.front();//获取队首节点
_queue.pop();//节点出队
//4. 唤醒生产者
pthread_cond_signal(&_cond_pro);
//5. 解锁操作
pthread_mutex_unlock(&_mutex);
}
private:
std::queue<int> _queue;
int _capacity;//定义队列最大节点数量
pthread_mutex_t _mutex;//实现互斥,保护_queue;
pthread_cond_t _cond_con;//实现同步,消费者等待队列
pthread_cond_t _cond_pro;//实现同步,生产者等待队列
};
②消费者与生产者的执行流:
#define MAX_THREAD 5
void *thr_consumer(void *arg)
{
BlockQueue *q = (BlockQueue*)arg;
while(1) {
//消费者处理数据
int data;
q->Pop(&data);
printf("消费者:%p 出队数据:%d\n", pthread_self(), data);
}
return NULL;
}
void *thr_productor(void *arg)
{
BlockQueue *q = (BlockQueue*)arg;
int i = 0;
while(1) {
//生产者生产数据
q->Push(i);
printf("生产者:%p 入队数据:%d\n", pthread_self(), i++);
}
return NULL;
}
int main()
{
//完成生产者与消费者模型
//创建生产者角色的线程以及消费者角色的线程
pthread_t ctid[MAX_THREAD], ptid[MAX_THREAD];
int ret , i;
BlockQueue q;
//创建消费者线程
for (i = 0; i < MAX_THREAD; i++) {
ret = pthread_create(&ctid[i], NULL, thr_consumer, (void*)&q);
if (ret != 0) {
printf("thread create error\n");
return -1;
}
}
//创建生产者线程
for (i = 0; i < MAX_THREAD; i++) {
ret = pthread_create(&ptid[i], NULL, thr_productor, (void*)&q);
if (ret != 0) {
printf("thread create error\n");
return -1;
}
}
//等待所有线程退出
for (i = 0; i < MAX_THREAD; i++) {
pthread_join(ctid[i], NULL);
pthread_join(ptid[i], NULL);
}
return 0;
}
2. 信号量的方式实现:
#include <cstdio>
#include <iostream>
#include <queue>
#include <pthread.h>
#include <semaphore.h>
#define MAX_QUEUE 5
#define MAX_THREAD 5
class RingQueue{
private:
std::vector<int> _queue;
int _capacity;
int _step_read; // 当前即将读取数据的位置的下标
int _step_write; // 当前即将写入数据的位置的下标
sem_t _sem_lock;// 用于实现互斥的信号量
// 使用这个计数器,实现对当前队列中的数据资源的数量进行计数;
// 如果<=0表示没有资源,则消费者会陷入等待
sem_t _sem_data;
// 使用这个计数器,实现对当前队列 中的空闲空间数量进行计数;
// 如果<=0表示队列满了,则生产者陷入等地啊
sem_t _sem_space;
public:
RingQueue(int max = MAX_QUEUE) : _queue(max), _capacity(max),
_step_read(0), _step_write(0){
//...初始化过程...
//互斥信号量的初始化
//pshare设置为0,表示当前用于线程间的同步互斥
//value信号量初值,初始化为1,数值最大不大于1,实现互斥
sem_init(&_sem_lock, 0, 1);
sem_init(&_sem_data, 0, 0);//数据资源初始为0
sem_init(&_sem_space, 0, max);//空闲空间初值就是节点数量
}
~RingQueue() {
//...销毁资源过程...
sem_destroy(&_sem_lock);
sem_destroy(&_sem_data);
sem_destroy(&_sem_space);
}
bool Push(int data) {
sem_wait(&_sem_space);//统计空间节点数量,自动判断是否有空闲空间,没有则阻塞
//互斥保护临界资源,注意上边的信号量不需要保护
//若保护了上边的信号量操作,反而回出问题:若先加锁再判断,有可能会陷入休眠而没解锁
sem_wait(&_sem_lock);
_queue[_step_write] = data;
_step_write = (_step_write + 1) % _capacity;
sem_post(&_sem_lock);
sem_post(&_sem_data);//入队数据之后,数据资源的数量增加一个,并且唤醒消费者
return true;
}
bool Pop(int *data) {
sem_wait(&_sem_data);//消费者判读数据资源数量,若<=0则回陷入等待 ,计数-1
sem_wait(&_sem_lock);
*data = _queue[_step_read];
_step_read = (_step_read + 1) % _capacity;
sem_post(&_sem_lock);
sem_post(&_sem_space);//空间资源多了一个,唤醒生产者
return true;
}
};
void *thr_consumer(void *arg)
{
RingQueue *q = (RingQueue*)arg;
while(1) {
//消费者处理数据
int data;
q->Pop(&data);
printf("消费者:%p 出队数据:%d\n", pthread_self(), data);
}
return NULL;
}
void *thr_productor(void *arg)
{
RingQueue *q = (RingQueue*)arg;
int i = 0;
while(1) {
//生产者生产数据
q->Push(i);
printf("生产者:%p 入队数据:%d\n", pthread_self(), i++);
}
return NULL;
}
int main()
{
//完成生产者与消费者模型
//创建生产者角色的线程以及消费者角色的线程
pthread_t ctid[MAX_THREAD], ptid[MAX_THREAD];
int ret , i;
RingQueue q;
//创建消费者线程
for (i = 0; i < MAX_THREAD; i++) {
ret = pthread_create(&ctid[i], NULL, thr_consumer, (void*)&q);
if (ret != 0) {
printf("thread create error\n");
return -1;
}
}
//创建生产者线程
for (i = 0; i < MAX_THREAD; i++) {
ret = pthread_create(&ptid[i], NULL, thr_productor, (void*)&q);
if (ret != 0) {
printf("thread create error\n");
return -1;
}
}
//等待所有线程退出
for (i = 0; i < MAX_THREAD; i++) {
pthread_join(ctid[i], NULL);
pthread_join(ptid[i], NULL);
}
return 0;
}