您的位置:首页 > 运维架构 > Linux

生产者消费者问题

2017-02-20 20:15 148 查看
生产者消费者模型:

在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据

由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程

等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。

    单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要

有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,

而消费者从缓冲区取出数据。大概的结构如下图。

                    



为了不至于太抽象,我们举一个寄信的例子(虽说这年头寄信已经不时兴,但这个例

子还是比较贴切的)。

假设你要寄一封平信,大致过程如下:

    1、你把信写好——相当于生产者制造数据

    2、你把信放入邮筒——相当于生产者把数据放入缓冲区

    3、邮递员把信从邮筒取出——相当于消费者把数据取出缓冲区

    4、邮递员把信拿去邮局做相应的处理——相当于消费者处理数据

优点:

    1.降低耦合

因为两者通过中间媒介缓冲区,所以耦合率降低,如果生产者/消费者代码有变化,

另一方受影响不大。

   2.支持并发(concurrency)

    生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的

(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一

消费者处理数据很慢,生产者就会浪费很多时间去等待。

    3.支持忙闲不均

    缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现

出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲

区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。

    为了充分复用,我们拿寄信的例子来说事。假设邮递员一次只能带走1000封
d592


信。万一某次碰上情人节(也可能是圣诞节)送贺卡,需要寄出去的信超过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.加锁后(生产一个,消费一个)



     之前的图稍微有点问题(本人不小心上传错图了,还请大家多多包涵!),现已改正!

到此结束,谢谢!

   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息