生产者与消费者模型
1.生产者与消费者模型
1.123规则
概念:
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,
而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生
产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个
阻塞队列就是用来给生产者和消费者解耦的。
- 一个线程安全的队列:互斥、同步
- 两种角色的线程:生产者&消费者
- 三个规则:生产者与生产者互斥、消费者与消费者互斥、生产者与消费者互斥+同步
2.应用场景
后端服务器常见的场景:
3.优点
- 生产者与消费者解耦
- 支持高并发
- 支持忙闲不均
4.代码实现
在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区
别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存
放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队
列进程操作时会被阻塞)
代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <queue>
using namespace std;
#define THREAD_COUNT 2
/*
* 1.线程安全队列
* */
class RingQueue{
public:
RingQueue(){
capacity_ = 10;
pthread_mutex_init(&que_lock_, NULL);
pthread_cond_init(&cons_cond_, NULL);
pthread_cond_init(&prod_cond_, NULL);
}
~RingQueue(){
pthread_mutex_destroy(&que_lock_);
pthread_cond_destroy(&cons_cond_);
pthread_cond_destroy(&prod_cond_);
}
/*
* 1.提供给生产者线程使用的接口
* */
void Push(int data){
pthread_mutex_lock(&que_lock_);
while(que_.size() >= capacity_){
pthread_cond_wait(&prod_cond_, &que_lock_);
}
que_.push(data);
printf("i am product thread : %p, i product %d\n", pthread_self(), data);
pthread_mutex_unlock(&que_lock_);
pthread_cond_signal(&cons_cond_);
}
/*
* 1.提供给消费者线程进行消费的接口
* */
void Pop(int* data){
pthread_mutex_lock(&que_lock_);
while(que_.size() <= 0){
pthread_cond_wait(&cons_cond_, &que_lock_);
}
*data = que_.front();
que_.pop();
printf("i am consume thread : %p, i consume %d\n", pthread_self(), *data);
pthread_mutex_unlock(&que_lock_);
pthread_cond_signal(&prod_cond_);
}
private:
//队列
queue<int> que_;
size_t capacity_;
//互斥锁
pthread_mutex_t que_lock_;
//同步
pthread_cond_t cons_cond_;
pthread_cond_t prod_cond_;
};
void* cons_start(void* arg){
RingQueue* rq = (RingQueue*)arg;
while(1){
int data;
rq->Pop(&data);
}
}
int g_data = 0;
pthread_mutex_t g_data_lock = PTHREAD_MUTEX_INITIALIZER;
void* prod_start(void* arg){
RingQueue* rq = (RingQueue*)arg;
while(1){
pthread_mutex_lock(&g_data_lock);
rq->Push(g_data);
g_data++;
//sleep(1);
pthread_mutex_unlock(&g_data_lock);
}
}
int main(){
RingQueue* rq = new RingQueue();
pthread_t cons[THREAD_COUNT], prod[THREAD_COUNT];
for(int i = 0; i < THREAD_COUNT; i++){
int ret = pthread_create(&cons[i], NULL, cons_start, (void*)rq);
if(ret < 0){
perror("pthread_create");
return 0;
}
ret = pthread_create(&prod[i], NULL, prod_start, (void*)rq);
if(ret < 0){
perror("pthread_create");
return 0;
}
}
for(int i = 0; i < THREAD_COUNT; i++){
pthread_join(cons[i], NULL);
pthread_join(prod[i], NULL);
}
delete rq;
return 0;
}
2.信号量
1.原理
资源计数器+PCB等待队列
资源计数器:
执行流获取信号量,获取成功,信号量计数器减一操作,获取失败,执行流放入到PCB等待队列
执行流释放信号量成功之后,计数器加1操作
条件变量到信号量的转变
2.接口
信号量接口:int sem_init(sem_t *sem,int pshared,unsigned int value);
sem:信号量,sem_t是信号量的类型
pshared:该信号量是用于线程间还是进程间
0:用于线程间,是全局变量
非0:用于进程间
value:资源的个数,初始化信号量计数器的,将信号量所用的资源在共享内存中进行开辟
等待接口:int sem_wait(sem_t *sem);
1.对资源计数器进行减1操作
2.判断资源计数器的值是否小于0
是:则阻塞等待,将执行流放到PCB等待队列中
不是:则接口返回
释放接口:int sem_post(sem_t *sem);
1.对资源计数器进行加1操作
2.判断资源计数器的值是否小于等于0
是:通知PCB等待队列
否:不用通知PCB等待队列,因为没有线程在等待
销毁接口:int sem_destroy(sem_t *sem);
问题:在该模型中,是先拿信号量还是先加锁?
一定是先获取信号量,再获取互斥锁。因为先获取信号量,才能知道资源的情况,保证互斥;
3.代码实现
#include <stdio.h>
#include <unistd.h>
#include <vector>
#include <pthread.h>
#include <semaphore.h>
using namespace std;
/*
* 定义线程安全的队列
* 环形队列(用数组模拟)
* 线程安全:
* 同步:信号量
* 互斥: 信号量
* */
#define CAPACITY 1
class Rinqueue{
public:
Rinqueue():vec_(CAPACITY){
capacity_ = CAPACITY;
sem_init(&sem_lock_, 0, 1);
sem_init(&sem_cons_, 0, 0);
sem_init(&sem_prod_, 0, CAPACITY);
pos_write_ = 0;
pos_read_ = 0;
}
~Rinqueue(){
sem_destroy(&sem_lock_);
sem_destroy(&sem_cons_);
sem_destroy(&sem_prod_);
}
void Push(int data){
sem_wait(&sem_prod_);
sem_wait(&sem_lock_);
printf("i am product %p, i product %d\n", pthread_self(), data);
vec_[pos_write_] = data;
pos_write_ = (pos_write_ + 1) % capacity_;
sem_post(&sem_lock_);
sem_post(&sem_cons_);
}
void Pop(){
sem_wait(&sem_cons_);
sem_wait(&sem_lock_);
int data = vec_[pos_read_];
pos_read_ = (pos_read_ + 1) % capacity_;
printf("i am thread %p, i consume %d\n", pthread_self(), data);
sem_post(&sem_lock_);
sem_post(&sem_prod_);
}
private:
vector<int> vec_;
//数组的容量大小
size_t capacity_;
//保证互斥的信号量
sem_t sem_lock_;
//消费者的信号量
sem_t sem_cons_;
//生产者的信号量
sem_t sem_prod_;
int pos_write_;
int pos_read_;
};
/*
* 创建两种角色的线程
* 1.生产者
* 2.消费者
* */
#define THREADCOUNT 1
void* cons_strat(void* arg){
Rinqueue* rq = (Rinqueue*)arg;
while(1){
rq->Pop();
sleep(2);
}
}
int g_data = 0;
void* prod_strat(void* arg){
Rinqueue* rq = (Rinqueue*)arg;
while(1){
rq->Push(g_data++);
sleep(1);
}
}
int main(){
Rinqueue* rq = new Rinqueue();
pthread_t cons[THREADCOUNT], prod[THREADCOUNT];
for(int i = 0; i < THREADCOUNT; i++){
int ret = pthread_create(&cons[i], NULL, cons_strat, (void*)rq);
if(ret < 0){
perror("pthread_create");
return 0;
}
ret = pthread_create(&prod[i], NULL, prod_strat, (void*)rq);
if(ret < 0){
perror("pthread_create");
return 0;
}
}
for(int i = 0; i < THREADCOUNT; i++){
pthread_join(cons[i], NULL);
pthread_join(prod[i], NULL);
}
return 0;
}