生产者消费者问题

生产者消费者模型:

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

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

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

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

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

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

                    

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

子还是比较贴切的)。

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

    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.加锁后(生产一个,消费一个)


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

到此结束,谢谢!

   

《生产者与消费者问题算法实现》 设计思想 因为有多个缓冲区,所以生产者线程没有必要在生成新的数据之前等待最后一个数据被消费者线程处理完毕。同样,消费者线程并不一定每次只能处理一个数据。在多缓冲区机制下,线程之间不必互相等待形成死锁,因而提高了效率。   多个缓冲区就好像使用一条传送带替代托架,传送带上一次可以放多个产品。生产者在缓冲区尾加入数据,而消费者则在缓冲区头读取数据。当缓冲区满的时候,缓冲区就上锁并等待消费者线程读取数据;每一个生产或消费动作使得传送带向前移动一个单位,因而,消费者线程读取数据的顺序和数据产生顺序是相同的。 可以引入一个count计数器来表示已经被使用的缓冲区数量。用hNotEmptyEvent 和hNotFullEvent 来同步生产者和消费者线程。每当生产者线程发现缓冲区满( count=BufferSize ),它就等待hNotEmptyEvent 事件。同样,当消费者线程发现缓冲区空,它就开始等待hNotEmptyEvent。生产者线程写入一个新的数据之后,就立刻发出hNotEmptyEvent 来唤醒正在等待的消费者线程;消费者线程在读取一个数据之后,就发出hNotFullEvent 来唤醒正在等待的生产者线程。 程序的设计思想大致为:设置一while循环,pi生产者访问临界区,得到权限访问缓冲区,如果缓冲区满的,则等待,直到缓冲区非满;访问互斥锁,当得到互斥锁且缓冲区非满时,跳出while循环,开始产生新数据,并把数据存放于Buffer缓冲区中,当数据存放结束则结束临界区;接着唤醒消费者线程;ci消费者访问临界区,得到权限访问缓冲区,如果缓冲区为空,没有可以处理的数据,则释放互斥锁且等待,直到缓冲区非空;当等到缓冲区非空时,跳出while循环;消费者获得数据,并根据所获得的数据按类别消费(当消费者获得的数据为大写字母时,则把大写字母转换成小写字母,并显示;当消费者获得的数据为小写字母时,则把小写字母转换成大写字母,并显示;当消费者获得的数据为字符0、1、2、……8、9时,把这些字符直接显示到屏幕;当消费者获得的数据为符号(+、-、*、\……)时,把这些符号打印成7行7列的菱形);处理完数据后,结束临界区;接着唤醒生产者线程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值