1、用多线程同步方法解决生产者消费者问题1.需求分析1.1 课程设计题目用多线程同步方法解决生产者消费者问题1.2 课程设计任务(1)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、当前指针位置和生产者消费者线程的标识符。(2)生产者和消费者各有两个以上。(3)多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。1.3 课程设计原理生产者和消费者问题是从操作系统中的许多实际同步问题中抽象出来的具有代表性的问题,它反映了操作系统中典型的同步例子,生产者进程(进程由多个线程组成)生产信息,消费者进程使用信息,由于生产者和消费者彼此独立,且运行速度不确定,所以很可能出现生
2、产者已产生了信息而消费者却没有来得及接受信息这种情况。为此,需要引入由一个或者若干个存储单元组成的临时存储区(即缓冲区),以便存放生产者所产生的信息,解决平滑进程间由于速度不确定所带来的问题。1.4 课程设计要求(1)有界缓冲区内设有20个存储单元,放入取出的数据项设定为120这20个整型数。(2)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、当前指针位置和生产者消费者线程的标识符。(3)生产者和消费者各有两个以上。(4)多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。1.5 实验环境系统平台:LINUX开发语言:C 开发工具:PC机一台2. 概要设计2.
3、1 课程设计方案概述本设计中设置一个长度为20的一维数组buff20作为有界缓冲区,缓冲区为0时,代表该缓冲区内没有产品,buffi=i+1表示有产品,产品为i+1。设置3个同步信号灯:一个说明空缓冲区的数目,用empty表示,其初值为有界缓冲区的大小20 ;另一个说明满缓冲区(即产品)的数目,用full表示,其初值为0。由于缓冲区是临界资源,必须互斥使用,所以还设置了一个互斥信号灯mutex,其初值为1。用这3个信号灯有效控制多个生产者线程和多个消费者线程的同步准确的运行。Main()函数中调用函数sem_init()对信号灯进行初始化;利用for语句循环创建5个producer(生产者)分
4、离线程和5个consumer(消费者)分离线程;Producer线程通过调用函数sem_wait(&empty)判断是否有空缓冲区。若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资源的操作权,当mntex为1时,便获得临界资源的操作权,可将产品放入缓冲区,及时输出缓冲区里的内容。然后依次调用函数sem_post(&mutex)和sem_post(&full)来释放缓冲区操作权和增加满缓冲区信号量的值;Consumer线程通过调用函数sem_wait(&full) 判断是否有满缓冲区。若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资
5、源的操作权,当mntex为1时,便获得临界资源的操作权,可从满缓冲区中取出产品消费,并及时输出缓冲区里的内容。然后依次调用函数sem_post(&mutex)和sem_post(&emptyl)来释放缓冲区操作权和增加空满缓冲区信号量的值。Producer和Consumer线程中均用缓冲区指针b指出当前是对哪一个缓冲区进行放入/取出操作;并调用pthread_self()函数来显示其自身的标识符。2.2 课程设计流程图设计中主要有三个模块,首先从主程序中进入,完成初始化及创建好生产者和消费者线程后,进入生产者或消费者模块,具体流程见程序设计流程图如图1所示: 图1 程序设计流程图3.详细设计3
6、.1 主程序模块主程序中利用函数sem_init()对信号灯进行初始化;利用for语句循环创建3个producer(生产者)分离线程和3个consumer(消费者)分离线程,程序段如下:int main(void) int i; initbuff(); for(i=0;i5;i+) pthread_create(&id1i,NULL,(void *)producer,(&i); /创建生产者线程 for(i=0;i5;i+) pthread_create(&id2i,NULL,(void *)consumer,(&i); /创建消费者线程 for(i=0;i5;i+) pthread_join
7、(id1i,NULL); pthread_join(id2i,NULL); exit(0); /等待生产者线程,消费者线程,结束主线程3.2 生产者程序模块Producer线程通过调用函数sem_wait(&empty)判断是否有空缓冲区。若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资源的操作权,当mntex为1时,便获得临界资源的操作权,可将产品放入缓冲区,及时输出缓冲区里的内容;并用指针b指出当前是对哪一个缓冲区进行放入/取出操作,且调用pthread_self()函数来显示其自身的标识符。然后依次调用函数sem_post(&mutex)和sem_post
8、(&full)来释放缓冲区操作权和增加满缓冲区信号量的值,程序段如下:void producer() /生产者 int pid=0; int j,k; pid=pthread_self(); /获得生产者标识符 while(1) for(j=0;j5;j+) if(pid=id1j) k=j+1; sem_wait(&empty); /P操作,判断缓冲区是否有空位置 sem_wait(&mutex); /P操作,获得对临界资源的操作权 if(p%21!=0) buffb=p%21; printf(producer %d produce:%dn,k,p%21); printbuff(); b+;
9、 p+; else p+; sem_post(&mutex); /V操作,释放临界资源 sem_post(&full); /V操作,满缓冲区信号灯加1 sleep(4); 3.3 消费者程序模块Consumer线程调用调用函数sem_wait(&full) 判断是否有满缓冲区。若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资源的操作权,当mntex为1时,便获得临界资源的操作权,可从满缓冲区中取出产品消费,并及时输出缓冲区里的内容,并用指针b指出当前是对哪一个缓冲区进行放入/取出操作,且调用pthread_self()函数来显示其自身的标识符。然后依次调用函数s
10、em_post(&mutex)和sem_post(&emptyl)来释放缓冲区操作权和增加空满缓冲区信号量的值,程序段如下:void consumer() /消费者 int cid=0; int j,k; cid=pthread_self(); /获得消费者标识符 while(1) for(j=0;j5;j+) if(cid=id2j) k=j+1; sem_wait(&full); /P操作,判断缓冲区是否已满 sem_wait(&mutex); /P操作,获得对临界资源的操作权 if(c%21!=0) c1=buffb-1; printf(consumer %d consume:%dn,k
11、,c1); buffb-1=0; printbuff(); b-; c+; else c+; sem_post(&mutex); /V操作,释放临界资源 sem_post(&empty); /V操作,释放一个空位置 sleep(6); 4.调试中遇到的问题及解决方案在调试过程中主要遇到三个问题:1.在设计刚完成的时候,我并没有用pid=pthread_self()和cid=pthread_self()来获得生产者和消费者的标识符,所以运行的结果是生产者和消费者的标识符都是随机的,后来问同学才找到这种调用方法。2.我想让生产者无限的生产,消费者无限的消费,所以就用了一个条件判断语句,判断的条件是
12、p%21!=0,而我写成了p/21!=0,所以运行的结果是生产者的生产是混乱的,没有规律。3.刚开始我的运行结果还有一个问题就是生产者生产超过21的产品后,产品号不是1,2等,而是21,22,后来检查后才发现printf(producer %d produce:%dn,k,p%21)中的产品号不是p%21,所以出现那种情况。5.运行结果显示结果如下:(部分运行结果)6.实验小结这次课程设计主要针对的是操作系统中的经典问题即:生产者消费者问题使用多线程控制,刚拿到设计题目时心里其实挺没底的,因为之前我对线程不是很了解,不过幸好老师上课的时候给了我们一个关于Linux多线程编程的文档,里面详细的介
13、绍了线程的一些基本命令,所以我在这次设计中基本上都用的是文档里的命令。从这次课程设计中,我对线程的概念以及一些命令都有了一定的理解和掌握,并且也知道了它与进程之间的区别,除此之外,通过这次课程设计让我对操作系统中的经典问题,生产者消费者问题有了更深的理解,在做设计的过程中,也对上课的内容加深了理解并进行了巩固。参考文献1庞丽萍.操作系统原理.华中科技大学出版社.2009年1月。2 蒋静 徐志伟.操作系统原理技术与编程M.北京:机械工业出版社,20043张红光 李福才.UNIX操作系统。机械工业出版社。2006年1月4汤子瀛等.计算机操作系统.西安电子科技大学出版社.2001年5月5付国瑜 杨武
14、 周敏.计算机操作系统原理及应用上机实验指导.重庆工学院计算机学院.2005年1月.附录:源程序清单#include#include#include#includeint buff20=0; /有界缓冲区定义int b=0; /缓冲区的输出指针int p=1;int c=1;int c1=0; /消费数据变量sem_t full; /缓冲区的数量信号灯sem_t empty; /缓冲区满信号灯sem_t mutex; /互斥信号灯pthread_t id15;pthread_t id25;void initbuff() /初始化信号灯 sem_init(&full,0,0); sem_init
15、(&empty,0,20); sem_init(&mutex,0,1); /初始化互斥信号灯 void printbuff() /打印缓冲区 int j; printf(datas in buff are:); for(j=0;j20;j+) printf(%d ,buffj); printf(n); void producer() /生产者 int pid=0; int j,k; pid=pthread_self(); while(1) for(j=0;j5;j+) if(pid=id1j) k=j+1; sem_wait(&empty); sem_wait(&mutex); if(p%21
16、!=0) buffb=p%21; printf(producer %d produce:%dn,k,p%21); printbuff(); b+; p+; else p+; sem_post(&mutex); sem_post(&full); sleep(4); void consumer() /消费者 int cid=0; int j,k; cid=pthread_self(); while(1) for(j=0;j5;j+) if(cid=id2j) k=j+1; sem_wait(&full); /看是否有数据在缓冲区 sem_wait(&mutex); /临界区 if(c%21!=0)
17、 c1=buffb-1; printf(consumer %d consume:%dn,k,c1); buffb-1=0; printbuff(); b-; c+; else c+; sem_post(&mutex); sem_post(&empty); sleep(6); int main(void) int i; initbuff(); for(i=0;i5;i+) pthread_create(&id1i,NULL,(void *)producer,(&i); /创建生产者线程 for(i=0;i5;i+) pthread_create(&id2i,NULL,(void *)consum
18、er,(&i); /创建消费者线程 for(i=0;i5;i+) pthread_join(id1i,NULL); pthread_join(id2i,NULL); exit(0); /等待生产者线程,消费者线程,结束主线程设计过程中质疑(或答辩)记载:1.生产者和消费者是如何从缓冲区中存放和读取数据的?答:生产者将生产的产品从头到尾存入缓冲区,消费者从缓冲区中最后一个开始读取,即从后往前取。2.设计中用到的三个信号灯各有什么作用?答:信号灯full是用来判断缓冲区中是否已满,如果满了就会发生阻塞,即生产者先暂停生产,信号灯empty是用来判断缓冲区是否还有产品,如果没有,消费者就暂停消费,互斥信号灯mutex是用来对临界资源的操作权进行控制的。指导教师评语: 签名: 2009 年7月03日
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1