提示:虚心求教
文章目录
- 1.实验方法
- 2.数据流图
- 3.实验源代码
- 4.运行截图
- 5.问题及解决办法
- 6.实验总结
1.实验方法
(1)程序说明:
本程序是模拟两个进程,生产者 (producer)和消费者(Consumer)工作。
生产者每次产生一个数据,送入缓冲区中。消费者每次从缓冲区中取走一个数据。缓冲区可以容纳 8个数据。因为缓冲区是有限的,因此当其满了时生产者进程应该等待,而空时,消费者进程应该等待 当生产者向缓冲区放入了一个数据,应唤醒正在等待的消费者进程,同样,当消费者取走一个数据后,应唤醒正在等待的生产者进程。就是生产者和消费者之间的同步。每次写入和读出数据时,都将读和写指针加一。当读写指针同样时,又一起退回起点。当写指针指向最后时,生产者就等待。当读指针为零时,再次要读取的消费者也应该等待。
为简单起见,每次产生的数据为 0-99 的整数,从0开始,顺序递增。两个进程的调度是通过运行者使用键盘来实现的。
(2)使用说明:
启动程序后,如果使用’p‘键则进行一次生产者进程,’c‘键则运行一次消费者进程。屏幕可观察俩个进程的状态和缓冲区变化的情况。
- 个人见解:
完美解决无法循环利用缓冲区问题:按生产序号顺序消费,生产多于管道个数的产品放进仓库(阻塞状态),当管道有空时唤醒仓库里的产品放入管道(队列——先进先出),按生产序号顺序消费,当无产品时不能消费。
2.数据流图
3.实验源代码
/***************************************************************/
/* PROGRAM NAME: PRODUCER_CONSUMER */
/* This program simulates two processes, producer which */
/* continues to produce message and put it into a buffer */
/* [implemented by PIPE], and consumer which continues to get */
/* message from the buffer and use it. */
/* The program also demonstrates the synchronism between */
/* processes and uses of PIPE. */
/***************************************************************/
#include <stdlib.h>
#define PIPESIZE 8
#define PRODUCER 0
#define CONSUMER 1
#define RUN 0 /* statu of process */
#define WAIT 1 /* statu of process */
#define READY 2 /* statu of process */
#define NORMAL 0
#define SLEEP 1
#define AWAKE 2
#include <stdio.h>
struct pcb {
char *name;
int statu;
int time;
}; /* times of execution */
struct pipetype {
char type;
int writeptr;
int readptr;
struct pcb *pointp; /* write wait point */
struct pcb *pointc; /* read wait point */
};
int pipe[PIPESIZE];
struct pipetype pipetb;
struct pcb process[2];
int count=0; //定义产品初始量
main()
{
int output,ret,i;
char in[2];
int runp(),runc(),prn();
pipetb.type = 'c'; pipetb.writeptr = 0; pipetb.readptr = 0;
pipetb.pointp = pipetb.pointc = NULL;
process[PRODUCER].name = "Producer\0";
process[CONSUMER].name = "Consumer\0";
process[PRODUCER].statu = process[CONSUMER].statu = READY;
process[PRODUCER].time = process[CONSUMER].time = 0;
output = 0;
printf("Now starting the program!\n");
printf("Press 'p' to run PRODUCER, press 'c' to run CONSUMER.\n");
printf("Press 'e' to exit from the program.\n");
for(i=0;i<1000;i++) {
in[0]='N';
while(in[0]=='N') {
scanf("%s",in);
if(in[0]!='e'&&in[0]!='p'&&in[0]!='c')
in[0]='N';
}
if(in[0]=='e') {
printf("Program completed!\n");
exit(0);
}
if(in[0]=='p'&&process[PRODUCER].statu==READY) {
//if(count<8) //有空闲位置,产品量加一
output = (output+1)% 100 ;
if((ret=runp(output,process,pipe,&pipetb,PRODUCER))==SLEEP)
pipetb.pointp = &process[PRODUCER];
if(ret==AWAKE) {
(pipetb.pointc)->statu=READY; pipetb.pointc=NULL;
runc(process,pipe,&pipetb,CONSUMER); //先前此处注释故错误3
process[CONSUMER].statu=READY;
}
}
if(in[0]=='c'&&process[CONSUMER].statu==READY) {
if((ret=runc(process,pipe,&pipetb,CONSUMER))==SLEEP){
pipetb.pointc = &process[CONSUMER];
}
if(ret==AWAKE) {
(pipetb.pointp)->statu=READY; pipetb.pointp=NULL;
runp(output,process,pipe,&pipetb,PRODUCER);//先前此处注释故错误2
//process[PRODUCER].statu=READY;
}
}
if(in[0]=='p'&&process[PRODUCER].statu==WAIT)
printf("PRODUCER is waiting, can't be scheduled.\n");
if(in[0]=='c'&&process[CONSUMER].statu==WAIT)
printf("CONSUMER is waiting, can't be scheduled.\n");
prn(process,pipe,pipetb); in[0]='N'; }
}
int runp(out,p,pipe,tb,t) /* run producer */
int out,pipe[],t;
struct pcb p[];
struct pipetype *tb;
{
p[t].statu = RUN;
printf("run PRODUCER. product %d ",out);
if(count>=8) {
p[t].statu=WAIT;
return(SLEEP);
}
pipe[tb->writeptr]=out;
count++;
tb->writeptr++;
tb->writeptr%=8; //保证输入指针处于队列之中
p[t].time++;
p[t].statu=READY;
if((tb->pointc)!=NULL)
return(AWAKE);
return(NORMAL);
}
int runc(p,pipe,tb,t) /* run consumer */
int pipe[],t;
struct pcb p[];
struct pipetype *tb;
{
int c;
p[t].statu = RUN;
printf("run CONSUMER. ");
if(count<=0) {
p[t].statu=WAIT;
return(SLEEP);
}
c = pipe[tb->readptr];
pipe[tb->readptr]=0; //取出,赋零值
count--; //产品量减一
tb->readptr++;
tb->readptr%=8; //保证输出指针处于队列之中
printf(" use %d ",c);
if(tb->readptr==tb->writeptr) //队列为空,指针回到队头
tb->readptr=tb->writeptr=0;
p[t].time++;
p[t].statu=READY;
if(tb->pointp!=NULL)
//if((tb->readptr)==0&&(tb->pointp)!=NULL)
return(AWAKE);
return(NORMAL);
}
int prn(p,pipe,tb)
int pipe[];
struct pipetype tb;
struct pcb p[];
{
int i,j;
printf("\n ");
for(i=0;i<PIPESIZE;i++)
printf("------ ");
printf("\n |");
for(i=0;i<PIPESIZE;i++)
if(pipe[i])
printf(" %2d |",pipe[i]);
else
printf(" |");
printf("\n ");
for(i=0;i<PIPESIZE;i++)
printf("------ ");
printf("\nwriteptr = %d, readptr = %d, ",tb.writeptr,tb.readptr);
if(p[PRODUCER].statu==WAIT)
printf("PRODUCER wait ");
else
printf("PRODUCER ready ");
if(p[CONSUMER].statu==WAIT)
printf("CONSUMER wait ");
else
printf("CONSUMER ready ");
printf("\n");
}
4.运行截图
5.问题及解决办法
Question1:
我一开始实验九个产品 一个管道只能放八个 所以九号产品在仓库(阻塞状态)。
当消费后管道有空时九号产品就可以唤醒放进第一个空间,但这时九号产品未被更改生产序号+1入队。
Answer1:
更改生产者和消费者的就绪态代码:
更改后如下图,为解决question1则有question2:
Question2:
我一开始实验九个产品 一个管道只能放八个 所以九号产品在仓库(阻塞状态),但writeptr与readptr指针在管道满队时未更改为0。
当消费后管道有空时九号产品就可以唤醒放进第一个空间,但这时九号产品未被唤醒(就绪状态)入队,此时的writeptr与readptr指针分别未指向1。
Answer2:
更改消费者的就绪状态代码:
更改后,可如下图满足初始需求:
Question3:
当第八号产品被消费后管道全空时,再消费产品就需等待生产,再生产时为第九号产品被等待的消费者消费,此时管道应仍为空,再生产第十号产品才被放入管道,但九号产品在管道里面未被等待的消费者消费,故消费者阻塞状态未唤醒需修改代码。
Answer3:
更改消费者的就绪状态代码:
更改后,可如下图满足初始需求:
6.实验总结
(1)运用循环队列(先进先出),多线程编程,wait()函数,类中的index、in、out。
(2)使用的数据结构:
进程控制块:进程名、进程状态、执行次数。
缓冲区:一个整数数组。
缓冲区说明:类型、读指针、写指针、读等待指针和写等待指针。
(3)生产者和消费者的阻塞状态的唤醒运行容易忘记!!!