生产者消费者问题

这是进程/线程同步与互斥中最简单、最经典的一个例子,也被叫做“有限缓冲区”问题。

假定现在有一个“有限缓冲池”(最讨厌这种fancy word-_-+,其实就是空间有限的一块地方吧),生产者负责往里面投放资源,消费者负责把资源拿走(这样的模型很常见,比如说打字,你就是一个生产者,负责往IO缓冲区写东西,处理这个缓冲区的程序就是消费者,负责把你输入的东西拿出来显示到屏幕上)。这里会出现几个问题:

1)缓冲区空了,则消费者不能继续读

2)缓冲区满了,则生产者不能继续写

解决的办法是:

1)如果缓冲区空了,我们应该让消费者停下来

2)如果缓冲区满了,应该让生产者停下来

(这不是废话吗。。。。。。。。。。。

可是不管是消费者还是生产者都不能一直停着不干活啊,所以核心问题就是实现同步:当一个停下的时候,另一个要在适当地时候唤醒它。

下面来看一个用条件变量互斥锁来演示消费者生产者问题的例子:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

struct msg{
	struct msg* next;
	int num;
};

struct msg* head;
pthread_cond_t has_product=PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;

void* producer(void* p){
	struct msg* mp;
	int i;
	for(i=0;i<20;i++){
		mp=malloc(sizeof(struct msg));
		mp->num=rand()%1000+1;
		printf("Produce %d\n",mp->num);
		pthread_mutex_lock(&lock);
		mp->next=head;
		head=mp;
		pthread_mutex_unlock(&lock);
		pthread_cond_signal(&has_product);
		sleep(rand()%5);
	}
}

void* consumer(void* p){
	struct msg* mp;
	int i;
	for(i=0;i<20;i++){
		pthread_mutex_lock(&lock);
		while(head==NULL)
			pthread_cond_wait(&has_product,&lock);
		mp=head;
		head=mp->next;
		pthread_mutex_unlock(&lock);
		printf("Consume %d\n",mp->num);
		free(mp);
		sleep(rand()%5);
	}
}

int main(char argc,char* argv[]){
	pthread_t pt,ct;
	srand(time(NULL));
	pthread_create(&pt,NULL,producer,NULL);
	pthread_create(&ct,NULL,consumer,NULL);
	pthread_join(pt,NULL);
	pthread_join(ct,NULL);
	return 0;
}
运行效果截图:

这个问题也可以用信号量来说明,代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
#include<time.h>

sem_t full,empty;
pthread_mutex_t mutex;
#define BUFFERSIZE 5

struct msgbuf{
	pid_t id;
	time_t mytime;
};

struct msgbuf msg[BUFFERSIZE];
int in=0,out=0;

void* producer(void* arg){
	int i;
	time_t rt;
	for(i=1;i<=10;i++){
		sem_wait(&empty);
		pthread_mutex_lock(&mutex);
		msg[in].id=pthread_self();
		time(&msg[in].mytime);
		in=(++in)%5;
		printf("生产者%d第%d次写消息:id=%u,time is %s\n",\
		arg,i,(unsigned)(msg[out].id),ctime(&msg[out].mytime)
		);
		pthread_mutex_unlock(&mutex);
		sem_post(&full);
		srand((unsigned)time(&rt));
		sleep(rand()%5);
	}
}

void* consumer(void* arg){
	int i;
	time_t rt;
	for(i=1;i<=10;i++){
		sem_wait(&full);
		pthread_mutex_lock(&mutex);
		printf("消费者%d第%d次读消息:id=%u,time is %s\n",\
		arg,i,(unsigned)(msg[out].id),ctime(&(msg[out].mytime))
		);
		out=(++out)%5;
		pthread_mutex_unlock(&mutex);
		sem_post(&empty);
		srand((unsigned)time(&rt));
		sleep(3+rand()%5);
	}
}

int main(char argc,char* argv[]){
	pthread_t pid1,pid2;
	pthread_t cid1,cid2;	
	sem_init(&full,0,0);
	sem_init(&empty,0,5);
	pthread_mutex_init(&mutex,NULL);
	pthread_create(&pid1,NULL,producer,NULL);
	pthread_create(&pid2,NULL,producer,NULL);
	pthread_create(&cid1,NULL,consumer,NULL);
	pthread_create(&cid2,NULL,consumer,NULL);
	pthread_join(pid1,NULL);
	pthread_join(pid2,NULL);
	pthread_join(cid1,NULL);
	pthread_join(cid2,NULL);
	pthread_mutex_destroy(&mutex);
	sem_destroy(&full);
	sem_destroy(&empty);
	return 0;
}
其中full表示消息的个数,empty表示空缓冲区个数。初始时full=0,empty=5,表示有0个消息,5个空缓冲区。信号量操作主要有P和V两个,P表示申请资源。对应sem_wait,V表示释放资源,对应sem_post。本质上来说,它们对信号量的value进行减操作或者加操作,根据结果判断要不要阻塞或者唤醒。(具体原理这里就略过了)

效果:




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值