生产者消费者模型:
在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据
由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程
等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要
有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,
而消费者从缓冲区取出数据。大概的结构如下图。
为了不至于太抽象,我们举一个寄信的例子(虽说这年头寄信已经不时兴,但这个例
子还是比较贴切的)。
假设你要寄一封平信,大致过程如下:
1、你把信写好——相当于生产者制造数据
2、你把信放入邮筒——相当于生产者把数据放入缓冲区
3、邮递员把信从邮筒取出——相当于消费者把数据取出缓冲区
4、邮递员把信拿去邮局做相应的处理——相当于消费者处理数据
优点:
1.降低耦合
因为两者通过中间媒介缓冲区,所以耦合率降低,如果生产者/消费者代码有变化,
另一方受影响不大。
2.支持并发(concurrency)
生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的
(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一
消费者处理数据很慢,生产者就会浪费很多时间去等待。
3.支持忙闲不均
缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现
出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲
区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
为了充分复用,我们拿寄信的例子来说事。假设邮递员一次只能带走1000封
信。万一某次碰上情人节(也可能是圣诞节)送贺卡,需要寄出去的信超过1000
封,这时候邮筒这个缓冲区就派上用场了。邮递员把来不及带走的信暂存在邮筒中,
等下次过来时再拿走。
生产者消费者模型中三种关系:
1.生产者和生产者(竞争关系)
2.消费者和消费者(竞争关系)
3.生产者和消费者(互斥与同步关系)
下面我们写个简单的例子,首先我们创建两个线程,线成1生产数据(PushFront),
线程2消费(PopFront)数据,我们使用pthread_mutex_lock(&mylock)进行加锁
(互斥锁)保护,以保证互斥,生产数据时不能消费数据,消费数据时不能生产数
据。
好了,不多说我们直接上代码:
#include<stdio.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
typedef struct Linknode
{
int _data;
struct Linknode* _next;
}node;
pthread_mutex_t mylock=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t mycond=PTHREAD_COND_INITIALIZER;
node* CreatNode(int data)
{
node* newNode=(node*)malloc(sizeof(node));
if(newNode==NULL){
perror("malloc failed\n");
return NULL;
}
newNode->_data=data;
newNode->_next=NULL;
return newNode;
}
void InitLink(node** _head)
{
*_head=CreatNode(0);
}
void PushFront(node *head,int data)
{
assert(head);
// assert(data);
node* newNode=CreatNode(data);
newNode->_next=head->_next;
head->_next=newNode;
}
void del_node(node* del)
{
free(del);
del=NULL;
}
int IsEmpty(node* head)
{
assert(head);
if(head->_next){
return 0;
}
return 1;
}
void PopFront(node* head,int* data)
{
assert(head);
if(IsEmpty(head)>0){
printf("list is empty\n");
return;
}
else{
node* del=head->_next;
*data=del->_data;
head->_next=del->_next;
del_node(del);
}
}
void DisplayLink(node *head)
{
assert(head);
node *cur=head->_next;
while(cur){
printf("%d ",cur->_data);
cur=cur->_next;
}
printf("\n");
}
void DestroyLink(node *head)
{
int data=0;
assert(head);
while(!IsEmpty(head)){
PopFront(head,&data);
}
free(head);
head=NULL;
}
void testLink()
{
node *head=NULL;
InitLink(&head);
int i=0;
int data=0;
for(;i<10;++i){
PushFront(head,i);
DisplayLink(head);
}
for(i=0;i<10;++i){
PopFront(head,&data);
DisplayLink(head);
}
DestroyLink(head);
}
void *product_run(void *arg)
{
int data=0;
node *head=(node *)arg;
while(1){
usleep(1234567);
data=rand()%1000;
//pthread_mutex_lock(&mylock);//加锁
PushFront(head,data);
//pthread_mutex_unlock(&mylock);//解锁
pthread_cond_signal(&mycond);
printf("product is done data=%d\n",data);
}
}
void *consumer_run(void *arg)
{
int data=0;
node *head=(node *)arg;
while(1){
//pthread_mutex_lock(&mylock);//加锁
while(!IsEmpty(head))
{
pthread_cond_wait(&mycond,&mylock);
}
PopFront(head,&data);
//pthread_mutex_unlock(&mylock);//解锁
printf("consumer is done data=%d\n",data);
}
}
void testMode()
{
node *head=NULL;
InitLink(&head);
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1,NULL,product_run,(void *)head);
pthread_create(&tid2,NULL,consumer_run,(void *)head);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
DestroyLink(head);
pthread_mutex_destroy(&mylock);
pthread_cond_destroy(&mycond);
}
int main()
{
testLink();
testMode();
return 0;
}
运行结果:
1.未加锁前:(此时消费未完,便有生产)
2.加锁后(生产一个,消费一个)
之前的图稍微有点问题(本人不小心上传错图了,还请大家多多包涵!),现已改正!
到此结束,谢谢!