一、.什么是生产者消费者模型?
在实际开发过程中,经常会遇到如下场景:
某个模块负责生产数据,这些数据由另一个模块来负责处理,产生数据的模块可以形象的看为生产者,处理数据的模块可以形象的看为消费者,但是生产者生产的东西需要存放在一个地方从而来供消费者使用,存放的地方可以是一个仓库,作为中介,联系啊生产者和消费者
生产者把生产的数据存放到缓冲区中,消费者从缓冲区中取出数据,从而就实现了生产者消费者模型
二、为什么需要生产者消费者模型
1.解耦(耦合就是依赖)
生产者和消费者之间的联系依赖于一个缓冲区,并没有直接联系,假设没有生产者消费者模型,生产者和消费者之间的联系靠生产者调用消费者的某个函数来实现,那么生产者和消费者之间就会产生依赖,如果消费者的代码改变了那就会影响到生产者耦合性(依赖性)就降低了。
2.支持并发
生产者直接调用消费者的某个方法,还有一个弊端,由于函数的调用时同步的(或者称做阻塞),那就会出现在消费者没有调用完函数之前,生产者就只能等待,从而生产者的时间,使用生产者消费者模型,生产者生产了数据放到缓冲区就好,就可以生产下一个数据,不需要管消费者什么时候取走数据,因为生产者和消费者是两个独立的主体
3.支持忙闲不均
缓冲区还有一个好处就是如果生产者生产的快了,消费者来不及处理的话,数据可以放到缓冲区中,生产者如果生产的慢了,缓冲区还有数据可以供消费者使用
三、生产者消费者模型的特点
1.一个交易场所
交易场所(存放数据的地方):可以是一个变量、一个数组、一个链表或者数据结构等
2.两种角色:
(1)生产者将数据放到交易场所中;
(2)消费者从交易场所中取数据;
3.三种关系:
(1)生产者与生产者之间是互斥关系;
(2)消费者与消费者之间是互斥关系;
(3)生产者与消费者之间是同步互斥关系;
ps:生产者生产的时候消费者不能消费
消费者消费的时候生产者不能生产
缓冲区空的话消费者不能消费
缓冲区满的化生产者不能生产
四、实现生产者消费者模型
使用链表作为交易场所,生产者生产一个结点头插到链表中,消费者在从链表中把插入的结点消费(删除),即就是头插的插入结点,头删的删除结点
consumers_producers.h
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#define PRODUCERS_COUNT 3//生产者的数量
#define CONSUMERS_COUNT 2//消费者的数量
pthread_mutex_t mutex;//声明互斥量
pthread_cond_t cond;//声明条件变量
pthread_t thread[CONSUMERS_COUNT+PRODUCERS_COUNT];
//封装链表中的结点
struct msg
{
int data;
struct msg*next;
};
//生产者生产
void*producer(void*arg);
//消费者消费
void*consumer(void*arg);
consumers_producers.c
#include"consumers_producers.h"
struct msg*head=NULL;
//生产者生产
void*producer(void*arg)
{
int num=*(int*)arg;
free(arg);
while(1){
printf("%d begin produce product......\n",num);
//1.构造结点
struct msg* m;
m=(struct msg*)malloc(sizeof(struct msg));
m->data=rand()%1000+1;//随机的产生一个数,范围是1-1000
printf("%d:> produce %d\n",num,m->data);
//2.给临界资源上锁
pthread_mutex_lock(&mutex);
//3.生产结点,就是给生产者消费者模型中的交易场所里存放数据,供消费者使用
m->next=head;//头插
head=m;//头插完成后,更新头结点
printf("%d end produce product......\n",num);
//4.交易场所中已有数据,通知消费者可以消费了,此通知是一对一的通知
pthread_cond_signal(&cond);
//5.解锁
pthread_mutex_unlock(&mutex);
sleep(2);//随机的睡眠几秒,范围是5秒之内
}
}
//消费者消费
void*consumer(void*arg)
{
int num=*(int*)arg;
free(arg);
while(1){
//1.给将要访问的临界资源上锁
pthread_mutex_lock(&mutex);
//2.交易场所中没有数据的话,那就一直循环的等待
while(head==NULL){
printf("%d waitting contion......\n",num);
pthread_cond_wait(&cond,&mutex);
}
printf("%d end wait condition......\n",num);
printf("%d begin consume product......\n",num);
//3.消费交易场所中的数据
struct msg* m;
m=head;//标记头结点,以便于头删
head=m->next;//更新头结点
//4.解锁
pthread_mutex_unlock(&mutex);
printf("%d:> consume %d\n",num,m->data);
free(m);//删除头结点
printf("%d end consume product......\n",num);
sleep(3);
}
}
test.c
#include"consumers_producers.h"
int main()
{
srand(time(0));
int i=0;
pthread_mutex_init(&mutex,NULL);//初始化互斥量
pthread_cond_init(&cond,NULL);//初始化条件变量
//创建生产者线程
for(i=0;i<PRODUCERS_COUNT;i++)
{
int *p=(int*)malloc(sizeof(int));
*p = i;
pthread_create(&thread[i],NULL,producer,(void*)p);
}
//创建消费者线程
for(i=0;i<CONSUMERS_COUNT;i++)
{
int* p=(int*)malloc(sizeof(int));
*p = i;
pthread_create(&thread[i+CONSUMERS_COUNT],NULL,consumer,(void*)p);
}
//等待线程
for(i=0;i<PRODUCERS_COUNT+CONSUMERS_COUNT;i++)
pthread_join(thread[i],NULL);
pthread_mutex_destroy(&mutex);//销毁互斥量
pthread_cond_destroy(&cond);//销毁条件变量
return 0;
}
运行结果:
编译命令:
gcc test.c consumers_producers.c -pthread
ps:记得一定要加 -pthread
在编译时遇到collect2: ld returned 1 exit status问题可参考博客:
https://blog.csdn.net/dangzhangjing97/article/details/80169103