【多线程系列】之生产者消费者问题

本文详细介绍了使用线程同步和互斥实现生产者-消费者问题的方法,通过具体代码示例展示了多生产者多消费者环境下如何确保数据一致性,避免竞态条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 生产者和消费者可以理解为:假设有一个仓库,仓库只有一个门,生产者每生产一件商品就要放入仓库;消费者每消耗一件商品就需要从仓库中取走一件商品;不管是生产者还是消费者,仓库每次只允许一个对象进入。因此生产者和消费者的流程:
  1. 生产者只要仓库未满就可以将生产出的商品放入其中;
  2. 消费者只要缓冲池未空就可以从仓库中取走商品;
  3. 仓库被占用时,任何进程都不能访问。

1. 相关代码
/*用线程的同步和互斥来实现"生产者-消费者"问题.*/
/*
* 多生产者多消费者多缓冲区 生产者和消费者不可同时进行
* 编译:gcc -o produce-consume produce-consume.c -lpthread
* 运行:./produce-consume
*/

#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#define M 10 // 缓冲数目
int in = 0;   // 生产者放置产品的位置
int out = 0; // 消费者取产品的位置
int buff[M] = {0}; // 缓冲初始化为0,开始时没有产品
int buffer=0; //单缓冲
sem_t empty_sem; // 同步信号量, 当满了时阻止生产者放产品
sem_t full_sem;   // 同步信号量, 当没产品时阻止消费者消费
pthread_mutex_t mutex; // 互斥信号量, 一次只有一个线程访问缓冲

int product_id = 0;   //生产者id
int consumer_id = 0; //消费者id
int product_sum=0;
int consumer_sum=0;
struct timeval start; //记录时间
struct timeval end;
unsigned long timer;
/* 打印缓冲情况 */
void print()
{
	int i;
	for(i = 0; i < M; i++)
   		printf("%d ", buff[i]);
}
/* 生产者方法 (多缓冲)*/ 
void *product(void * arg)
{
	int id = ++product_id;
	int t=*(int *)arg;   //t生产者生产时间
	while(1)
	{
  		 // 用sleep的数量可以调节生产和消费的速度,便于观察
  	 	sleep(t);
  	 	//sleep(1);		

 	  	sem_wait(&empty_sem);
   		pthread_mutex_lock(&mutex);

   		gettimeofday(&end,NULL);   //生产开始时间
		in = in % M;
		product_sum++;
		timer = 1000000 * (end.tv_sec-start.tv_sec)+ end.tv_usec-start.tv_usec;		
   		printf("time:%ldus product_%d in %d. buffer: ", timer,id, in+1);
		//printf("timer = %ld us\n",timer);
  		buff[in] = 1;  
   		print();
		printf("\t%d\n",product_sum);  
   		++in;

   		pthread_mutex_unlock(&mutex);
   		sem_post(&full_sem);  
	}
}

void *consumer(void * arg)
{
	int id = ++consumer_id;
	int t=*(int *)arg;
	while(1)
	{
  	 	// 用sleep的数量可以调节生产和消费的速度
 	 	sleep(t); 
   		sem_wait(&full_sem);
  		pthread_mutex_lock(&mutex);

   		gettimeofday(&end,NULL); //消费生产时间
		consumer_sum++;
		out = out % M;    
		timer = 1000000 * (end.tv_sec-start.tv_sec)+ end.tv_usec-start.tv_usec;
   		printf("time:%ldus consumer_%d in %d. buffer: ", timer,id, out+1); 
   		buff[out] = 0;
   		print();
		printf("\t%d\n",consumer_sum);
  	 	++out;	

  		pthread_mutex_unlock(&mutex);
  		sem_post(&empty_sem);
	}
}

/*配置生产者消费者参数,生产者和消费的数量以及时间*/
void config(int *N1,int *N2,int *T1,int *T2)
{
	*N1 = 10;
	*N2 = 10;
	*T1 = 1;
	*T2 = 1;
}

/*选择菜单*/
void menu()
{
	printf("------------------------------------------------------------\n");

	printf("-------------------------------------------------------------\n");
	printf("\n");
}

/*多缓冲*/
void f1(int N1,int N2,int T1,int T2,void *product,void *consumer)                                     
{
			//N1,N2分别是生产者消费者数目
			//T1,T2分别是生产消费时间
	pthread_t id1[N1];
	pthread_t id2[N2];
	int i;
	int ret1[N1];
	int ret2[N2];

	// 初始化同步信号量
	int ini1 = sem_init(&empty_sem, 0, M); //信号量将被进程内的线程共享
	int ini2 = sem_init(&full_sem, 0, 0);  
	if(ini1 && ini2 != 0)
	{
 		printf("sem init failed \n");
		exit(1);
	} 
	//初始化互斥信号量 
	int ini3 = pthread_mutex_init(&mutex, NULL);
	if(ini3 != 0)
	{
   		printf("mutex init failed \n");
   		exit(1);
	} 
	// 创建N1个生产者线程
	for(i = 0; i < N1; i++)
	{
		ret1[i] = pthread_create(&id1[i], NULL, product,&T1);
		if(ret1[i] != 0)
   		{
    			printf("product%d creation failed \n", i);
    			exit(1);
   		}
	}
	//创建N2个消费者线程
	for(i = 0; i < N2; i++)
	{
		ret2[i] = pthread_create(&id2[i], NULL, consumer, &T2);
  		if(ret2[i] != 0)
   		{
    			printf("consumer%d creation failed \n", i);
    			exit(1);
   		}
	}
	//销毁线程
	for(i = 0; i < N1; i++)
	{
		pthread_join(id1[i],NULL);
	}
	for(i = 0; i < N2; i++)
	{
   		pthread_join(id2[i],NULL);
	}
	
	exit(0); 
}

int main(int argc,char* argv)
{
	
	
	menu();
	int c;	    //选择功能
	scanf("%d",&c);
	int N1,N2,T1,T2;
	config(&N1,&N2,&T1,&T2);

	f1(N1,N2,T1,T2,product,consumer);


	return 0;
}
 

摘抄网址:https://github.com/cighao/produce-consume/blob/master/produce-consume.c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值