成电linux课程作业,多线程生产者消费者问题

问题描述

问题描述如图:
在这里插入图片描述

代码思想

 生产者消费者使用管道进行通信,每次读写1024*sizeof(int)大小的数据。
  int fd[10][2];//管道数组
  int fd_empty[10]={0};//消费者线程判断管道是否为空数组
  生产者要判断管道是否为空,消费者要判断管道内是否含有数据,因此追加声明一个fd_empty[]数组来让生产者消费者线程判断。
  由于只有一个生产者,所以生产的时候是顺序生产的,即文件中的数据块为1,2,3,4…。为了生产效率,生产时会遍历管道,寻找到为空的管道将数据填入其中,消费者消费时,会遍历管道,寻找到不为空的管道进行消费。由于生产者生产时填入管道中的数据为乱序,比如1号管道中的数据块可能为1,2,8。而消费者消费时读取到的数据块也无先后顺序,比如先读取到了2号数据块,再读取到1号数据块,此时如果像生产者那样顺序写入文件的话,得到的结果肯定和生产者生产的结果不一样。所以我们需要得到生产者生产的数据块所对应的位置信息。所以我们将fd_empty[]中存储位置信息,比如生产者第n次生产的数据块放入第m个管道中,那么fd_empty[m]=n,消费者可以据此知悉自己所读的数据块的位置是在哪里。
  最后用

pwrite(fp,w, once_number* sizeof(int), location*4096);

  将数据块写入指定的位置

代码

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/wait.h>
#include<fcntl.h>
#include<sys/stat.h>
int fd[10][2];//管道数组
int fd_empty[10]={0};//消费者线程判断管道是否为空数组
int pthread_number=10;//消费者数量
int once_number=1024;//一次读写大小
int all_ready_read=0;//目前消费者消费次数
int loop_number=262144;//生产(消费)次数
pthread_mutex_t mutex_fd_empty = PTHREAD_MUTEX_INITIALIZER;//给fd_empty数组上锁
void *producer()
{          
int i=0;
int *fp; 
fp=open("/home/wsk/文档/linuxhomework2/data.txt", O_RDWR |O_CREAT|O_APPEND,S_IRWXU);//生产者写入文件
int fd_number=0;//
int flag=0;//判断是否有管道空闲flag
while(i<loop_number)
{	  
	   flag=0;
	   int *w = (int *)malloc(once_number* sizeof(int));//申请写入数组
	   //生成随机内容的数组
	   for(int j=0;j<once_number;j++)
	   {
	    w[j]=rand()%10;
	   }
	   //寻找是否有管道内没有内容
	   for(fd_number=0;fd_number<10;fd_number++)
	   {
	   	if(fd_empty[fd_number]==0)//找到为空管道
	   	{
	   		fd_write_number=fd_number;
	   		flag=1;//有管道可以进行生产
	   		i=i+1;//生产了一次
	   		break;
	   	}
	   }
	   if(flag==1)
	   {
	  	   //生产
		   write(fd[fd_write_number][1],w,once_number* sizeof(int));//向管道内写数据
		   write(fp,w,once_number* sizeof(int));//向生产文件内写数据
		   pthread_mutex_lock(&mutex_fd_empty);
		   printf("fd_write_number=%d\n",fd_write_number);//输出现在在第几管道生产
		   fd_empty[fd_write_number]=i;//传给消费者位置信息(i-1)这里不用i-1而用i是为了下面判断的时候判断大于0的时候表示管道内有数据 用i的话i=1时候 i-1=0 消费者会错过第一次的生产内容
		   pthread_mutex_unlock(&mutex_fd_empty);
		   printf("i=%d\n",i-1);//输出现在生产的次数
	   }
}
close(fp);
}
void *consumer()
{
int fp;
int flag=0;
if((fp=open("/home/wsk/文档/linuxhomework2/data2.txt",O_RDWR |O_CREAT,S_IRWXU))==-1)//消费者写入文件
	   {
	  	printf("can't open the file");
	   }
     int *w = (int *)malloc(once_number* sizeof(int));
     int location=0;//消费者写入文件的位置
     int fd_read_number=0;//读取管道号码
     int j=0;
   
     while(all_ready_read<loop_number)
     {	
     	flag=0;
     	fd_read_number=0;
     	location=0;
		pthread_mutex_lock(&mutex_fd_empty);
		//判断管道有内容的位置
     	for(j=0;j<10;j++)
     	{
     		 
     		if(fd_empty[j])
     		  { 
     		  	flag=1;
     		  	fd_read_number=j;//把位置信息传给fd_read_number
     		   	break;
     		   }
     	}    	
     if(flag==1){
     	if(read(fd[fd_read_number][0],w,once_number* sizeof(int)))//从管道中读出数据到w指向的数组中
     	{
     		printf("pthread_number=%d all_ready_read=%d\n",pthread_self(),all_ready_read);//输出第几个线程和当前的消费次数
     		printf("fd_read_number=%d\n",fd_read_number);//读取管道号码
     		location=fd_empty[fd_read_number]-1;//写入文件的位置
     		printf("location=%d\n",location);
     		pwrite(fp,w, once_number* sizeof(int), location*4096); //写入到消费者文件中
     		all_ready_read=all_ready_read+1;	
     		fd_empty[fd_read_number]=0;	
     	}
     }
     pthread_mutex_unlock(&mutex_fd_empty);
     }
     close(fp);
 }
     
int main()
{
  int i=0;
  for(i=0;i<10;i++)
  {
  	if(pipe(fd[i])<0)//创建管道
  	{
  		printf("pipe %d error",i);
  	}
  	fd_empty[i]=0;//初始化fd_empty数组
  }
  pthread_t tidconsumer[pthread_number];
  pthread_t tidproducer;
  pthread_create(&tidproducer,NULL,producer,NULL);//创建生产者线程
  //创建消费者线程
  int pthread_number2=0;
  while(pthread_number2<pthread_number)
  {
  	pthread_create(&tidconsumer[pthread_number2],NULL,consumer,NULL);
  	pthread_number2=pthread_number2+1;
  }
  pthread_join(tidproducer,NULL);//判断消费者线程是否跑完
  printf("producer done/n");
  pthread_number2=0;
  //判断生产者线程是否跑完
  while(pthread_number2<pthread_number)
  {
  	pthread_join(tidconsumer[pthread_number2],NULL);
  	pthread_number2=pthread_number2+1;
  }
  
  printf("consumer done/n");
  
  return 0;

}

运行过程

在这里插入图片描述

结果

在这里插入图片描述
在这里插入图片描述

用md5和diff判断文件内容相同
在这里插入图片描述

踩坑

1.首先是在文件读写方面
在这里插入图片描述
在这里插入图片描述

fp=open("/home/wsk/文档/linuxhomework2/data2.txt",O_RDWR |O_CREAT,S_IRWXU))

 这里在最后加上了S_IRWXU,是为了给文件所有权限,不然创建出来的文件是上锁的状态(只读或者不可读不可写),不能进行后续的操作。
2.内存分配
 每次写入读出都是1024字节,这里使用了动态分配即:malloc

 int *w = (int *)malloc(once_number* sizeof(int));

 如果是声明命名w[1024]也可以。但是直接声明w[1024]是在栈内分配的内存,而用malloc是在堆上分配的内存。栈上分配内存存在着一个上限,而堆上分配内存的上限比栈上分配高。
3.关于上锁
  由于生产者生产完的产品放在管道中供消费者去品尝,所以二者都要对fd_empty数组进行操作(生产者生产完了里面放位置信息,消费者消费结束后放0)。因此要对这个共享数组进行上锁。
  此外由于消费者线程有10个,他们之间也是并行的,所以消费者们会存在对管道的争夺:比如现在有一个管道内含有数据,因为程序写的是先消费再置0,所以有可能一个消费者正在消费的时候,将其切出,另一个消费者获得了同样的管道接口,结果等第二个消费者想消费的时候管道里早已空无一物(被第一个消费者消费掉了)。因此为了避免这个问题将消费过程整体上锁。
 关于为什么不读出管道后马上置0,让其他消费者知道该管道被读取了:
 因为生产者和消费者也是并行的,可能存在消费者置0后还没来得及读,生产者以为管道内空无一物了,将其中的数据覆盖掉,所以要等消费完成后再进行置0。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值