一,设计要求
有界缓冲区内设有10个存储单元,放入/取出的数据项设定为1~10这10个整形数。要求每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、当前指针位置和生产者/消费者标识符。
二,实验环境
服务器:linux
客户端:windows98+telnet
开发语言:c
三,设计思想
通过本程序实现对生产者与消费者的同步运行,有效的控制它们对有界缓冲区的操作。本程序的实现涉及到两个主要问题,一,就是怎么实现生产着与消费者的互斥操作。二,怎么实现对10个缓冲区的充分利用。
本程序的设计思想大体如下:
有界缓冲区就一个一维长度为10的数组,用两个指针in,out来控制该区哪里的数据,往哪里添加数据。In用与指向最后放进去的数据,out用于指向第一个放进去的数据。
所以用以下算法就可以实现对10缓冲区的100%利用。
if(in==out&&buffer[in]==0)/*判断是否是缓冲没数据,此时的in指针不向后移*/
buffer[in]=nextproduced;
else /*否则in指针向后移一位*/
{
buffer[(in+1)%BUFFER_SIZE]=nextproduced;
in=(in+1)%BUFFER_SIZE;
}
以上为添加数据的控制,以下是消费数据的控制。
if(in==out&&buffer[out]!=0)/*判断in与out是否指向一个区,且不为空,此时out指针不移动*/
buffer[out]=0;
else /*否则,out指针向后移一位*/
{
buffer[out]=0;
out=(out+1)%BUFFER_SIZE;
}
对于进程的互斥可以用信号灯semaphore实现,本程序用sem_t定义了一个信号灯sem,在生产中使用sem_post(&sem),消费者中使用sem_wait(&sem)实现了只有先生产有足够的资源时,消费者才能消费。
四,调试报告
在调试过程中出现了不少的语法上的错误与逻辑上的错误,以下我选摘了一部分问题并加一解释。
在windows下使用turboc2编译程序的时候使用random函数是可以直接通过参数的形式传递给random,如random(10)产生0-9的随机数,但如果在linux中这样使用的话就会出现以下错误提示:course_design.c: In function `produce':
course_design.c:21: too many arguments to function `random'
故后来改为用random()%10+1这样的方式来产生1-10的随机数。
course_design.c:13: parse error before "sem"
course_design.c:13: warning: data definition has no type or storage class
少了头文件#include"semaphore.h"
course_design.c: In function `produce':
course_design.c:32: parse error before "pthread_t"
course_design.c: In function `consume':
course_design.c:51: parse error before "pthread_t"
由于在produce与consume中都调用了display函数,本应该用display(id),但由于粗心使用display(pthread_t id)所以出现了上述错误提示。
也曾经在定义线程是把pthread_t写成,lpthread而出现以下提示错误
course_design.c:16: parse error before "id"
course_design.c: In function `produce':
course_design.c:32: `id' undeclared (first use in this function)
course_design.c:32: (Each undeclared identifier is reported only once
course_design.c:32: for each function it appears in.)
付出了不少的时间找出原因
以上都是些语法方面的问题,所以查错起来也比较方便。但更重要的是逻辑上的错误,它编译通过,能运行,但结果有问题。这往往是算法方面的问题。
这方面的问题出现在:
一,线程的互斥出现问题。如,sem_post(&sem);位置没放好,特别是当放到while(1)的循环外面是就会出现一次性的生产完。sem_wait(&sem);也回出现相类似的问题。
二,问题出现在in与out的控制上,如果考虑不完全很容易出现10个缓冲区只能利用其中的8个或者9个空间。当把程序调试成功本人发现以前犯的错误很幼稚,在加上当时出现的问题也是实在是太多了,一个小小的失误或者考虑不周都会失败,所以现在就不一一列举了。
把以上的问题解决,程序能够基本上正确运行,由于开始只创建了2个线程,所以运行的同步效果不是很好,在老师的提示下,我后来创建了8个线程,后来我终于理解了老师的意思,线程越多它们抢占系统的资源也就越激烈,所以会产生跟好的同步效果。
五,输出结果
buffer[7]=2
buffer[8]=8
buffer[9]=5
in=7
out=7
consume an item:the id of thread is 134519388
buffer[0]=1
buffer[1]=7
buffer[8]=8
buffer[9]=5
in=7
out=7
produce an item:the id of thread is 134519380
buffer[0]=1
buffer[1]=7
buffer[7]=8
buffer[8]=8
buffer[9]=5
in=7
out=7
consume an item:the id of thread is 134519384
buffer[0]=1
buffer[1]=7
buffer[8]=8
buffer[9]=5
in=7
out=7
produce an item:the id of thread is 134519380
buffer[0]=1
buffer[1]=7
buffer[7]=2
buffer[8]=8
buffer[9]=5
in=7
out=7
consume an item:the id of thread is 134519468
buffer[0]=1
buffer[1]=7
buffer[8]=8
buffer[9]=5
in=7
out=7
produce an item:the id of thread is 134519360
buffer[0]=1
buffer[1]=7
buffer[7]=5
buffer[8]=8
buffer[9]=5
in=7
out=7
consume an item:the id of thread is 134519388
buffer[0]=1
buffer[1]=7
buffer[8]=8
buffer[9]=5
in=7
out=7
produce an item:the id of thread is 134519360
buffer[0]=1
buffer[1]=7
buffer[7]=2
buffer[8]=8
buffer[9]=5
in=7
out=7
consume an item:the id of thread is 134519384
buffer[0]=1
buffer[1]=7
buffer[8]=8
buffer[9]=5
in=7
out=7
produce an item:the id of thread is 134519360
buffer[0]=1
buffer[1]=7
buffer[7]=8
buffer[8]=8
buffer[9]=5
in=7
out=7
consume an item:the id of thread is 134519392
buffer[0]=1
buffer[1]=7
buffer[8]=8
buffer[9]=5
in=7
out=7
[lc@Hawk course_design]$
六,源代码
/*本实验用信号量实现的,创建了两个线程,一个生产者线程,一个消费线程*/
#include"stdlib.h"
#include"pthread.h"
#include"semaphore.h"
#include"time.h"
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE]; /*缓冲区*/
int in=0;/*输入指针,指向最后插入的数据*/
int out=0;/*输出指针,指向第一个插入的数据*/
pthread_t t1,t2,t3,t4,t5,t6,t7,t8;
sem_t sem;/*信号灯*/
void produce(pthread_t id)/*生产者函数,本程序通过循环队列直接实现对缓冲的完全使用*/
{
int nextproduced;/*用于装插入数据的临时变量*/
while(1)
{
nextproduced=random()%10+1;/*为了产生1-10的随机数,通过模10的方法*/
while(((in+1)%BUFFER_SIZE)==out)/*判断是否还有插入的空间*/
;
printf("/nproduce an item:");
if(in==out&&buffer[in]==0)/*判断是否是缓冲没数据,此时的in指针不向后移*/
buffer[in]=nextproduced;
else /*否则in指针向后移一位*/
{
buffer[(in+1)%BUFFER_SIZE]=nextproduced;
in=(in+1)%BUFFER_SIZE;
}
display(id);/*调用显示缓冲区的状态函数*/
sem_post(&sem);/*增加一个资源*/
}
}
void consume(pthread_t id)
{
while(1)
{
sem_wait(&sem);/*减少一个资源*/
printf("/nconsume an item:");/*显示线程的id号*/
if(in==out&&buffer[out]!=0)/*判断in与out是否指向一个区,且不为空,此时out指针不移动*/
buffer[out]=0;
else /*否则,out指针向后移一位*/
{
buffer[out]=0;
out=(out+1)%BUFFER_SIZE;
}
display(id);
}
}
int display(pthread_t id)
{
int i;
printf("the id of thread is %d",id);
for(i=0;i<10;i++)
{
if(buffer[i]!=0)/*本程序通过判断区中的数据是否为0来判断是否有数据*/
printf("/nbuffer[%d]=%d",i,buffer[i]);
}
printf("/nin=%d",in);
printf("/nout=%d",out);
}
int main(void)
{
sem_init(&sem,0,0);/*初始化信号量*/
pthread_create(&t1,NULL,(void *)produce,&t1);/*创建生产者线程*/
pthread_create(&t2,NULL,(void *)consume,&t2);/*创建消费者线程*/
pthread_create(&t3,NULL,(void *)produce,&t3);/*创建生产者线程*/
pthread_create(&t4,NULL,(void *)consume,&t4);/*创建消费者线程*/
pthread_create(&t5,NULL,(void *)produce,&t5);/*创建生产者线程*/
pthread_create(&t6,NULL,(void *)consume,&t6);/*创建消费者线程*/
pthread_create(&t7,NULL,(void *)produce,&t7);/*创建生产者线程*/
pthread_create(&t8,NULL,(void *)consume,&t8);/*创建消费者线程*/
pthread_join(t1,NULL);/*防止程序过早退出*/
}
七,总结
通过本次程序的准备,设计,调试与运行,本人学会了不少的知识。首先加深了我对线程与进程理解,消除了对多线程软件设计的神秘性。在准备与设计阶段出现不少的障碍,可能是做系统级别的程序比较少的缘故,除了老师附带的有关linux多线程的编程资料,我还在网上搜索了不少的资料,但最后还是用4.4信号灯实现的。对于缓冲区的完全的利用又考验了我对数据结构的理解,这里我用了循环队列的思想实现了。更重要的是问题是出现在调试阶段。上面第四点的调试报告只是其中的一部份,这里充分考验了我的c语言的基本功。最令人激动的是在自己与同学的帮助下,我的程序成功的运行了,虽然效果没有达到预想的。