1、学 号: 课 程 设 计题 目 生产者和消费者问题学 院 计算机科学与技术专 业班 级姓 名指导教师 吴利军2013 年 1 月 16 日课程设计任务书学生姓名: 指导教师: 吴利军 工作单位: 计算机科学与技术学院 题 目: 进程同步模拟设计生产者和消费者问题 初始条件:1预备内容:阅读操作系统的进程管理章节内容,对进程的同步和互斥,以及信号量机制度有深入的理解。2实践准备:掌握一种计算机高级语言的使用。要求完成的主要任务: (包括课程设计工作量及其技术要求,以及说明书撰写等具体要求)1模拟用信号量机制实现生产者和消费者问题。2设计报告内容应说明: 需求分析; 功能设计(数据结构及模块说明)
2、 ; 开发平台及源程序的主要部分; 测试用例,运行结果与运行情况分析; 自我评价与总结:i)你认为你完成的设计哪些地方做得比较好或比较出色;ii)什么地方做得不太好,以后如何改正;iii)从本设计得到的收获(在编写,调试,执行过程中的经验和教训) ;iv)完成本题是否有其他方法(如果有,简要说明该方法) ;时间安排:设计安排一周:周 1、周 2:完成程序分析及设计。周 2、周 3:完成程序调试及测试。周 4、周 5:验收、撰写课程设计报告。(注意事项:严禁抄袭,一旦发现,一律按 0 分记)指导教师签名: 年 月 日系主任(或责任教师)签名: 年 月 日生产者消费者问题(the producer
3、-consumer problem)1. 需求分析1.1 问题描述:一组生产者向一组消费者提供消息,它们共享一个有界缓冲区 n,生产者向其中投放消息,消费者从中取得消息。1.2 规则: 对于生产者进程:产生一个数据,当要送入缓冲区时,要检查缓冲区是否已满,若未满,则可将数据送入缓冲区,并通知消费者进程;否则,等待; 对于消费者进程:当它去取数据时,要看缓冲区中是否有数据可取,若有则取走一个数据,并通知生产者进程,否则,等待。 缓冲区是个临界资源,因此,诸进程对缓冲区的操作程序是一个共享临界区,所以,还有个互斥的问题。1.3 信号灯设置 :两个同步信号灯-empty :表示空缓冲区的数目,初值为
4、有界缓冲区的大小 n; full : 表示满缓冲区( 即信息)的数目,其初值为 0;一个互斥信号灯-mutex:互斥信号灯,初值为 1。 1.4 同步描述:1.5 程序描述:main( ) int full=0; /*满缓冲区的数目 */int empty=n;/* 空缓冲区的数目 */int mutex=1;/* 对有界缓冲区进行操作的互斥信号灯*/cobeginp1 ( ); p2( );coendp1() while(生产未完成 )生产一个产品;p(empty);p(mutex);送一个产品到有界缓冲区;v(mutex);v(full); p2( ) while(还要继续消费 ) p(f
5、ull); p(mutex);从有界缓冲区中取产品; v(mutex); v(empty); 消费一个产品; 1.6 C+语言程序模拟用信号量机制实现生产者和消费者问题:本次课程设计主要通过 C+模拟信号量制中各个进程,及各进程之间的互斥、同步关系,来实现生产者和消费者问题。2. 功能设计2.1 设计目的:通过实验模拟生产者和消费者之间的关系,了解并掌握他们之间的关系及其原理。由此增加对进程同步的问题的了解。具体如下:1)掌握基本的同步互斥算法,理解生产者和消费者模型;2)了解 windows 中多线程(多进程)的并发执行机制,线程(进程)间的同步和互斥;3)学习使用 windows 中基本的
6、同步对象,掌握相应的 API。2.2 设计功能:利用模拟用信号量机制实现生产者和消费者问题:通过用户控制取进程和放进程,反应生产者和消费者问题中所涉及的进程的同步与互斥。2.3 数据结构:2.4 模块说明:const unsigned short SIZE_OF_BUFFER = 10; /缓冲区长度 unsigned short ProductID = 0; /产品号 unsigned short ConsumeID = 0; /将被消耗的产品号 unsigned short in = 0; /产品进缓冲区时的缓冲区下标 unsigned short out = 0; /产品出缓冲区时的缓冲
7、区下标 int g_bufferSIZE_OF_BUFFER; /缓冲区是个循环队列 bool g_continue = true; /控制程序结束 HANDLE g_hMutex; /用于线程间的互斥 HANDLE g_hFullSemaphore; /当缓冲区满时迫使生产者等待 HANDLE g_hEmptySemaphore; /当缓冲区空时迫使消费者等待 DWORD WINAPI Producer(LPVOID); /生产者线程 DWORD WINAPI Consumer(LPVOID); /消费者线程 3. 开发平台及源程序的主要部分:3.1 开发平台:基于 VS2010 开发平台的
8、 C+编程3.2 源程序的主要部分:#include #include const unsigned short SIZE_OF_BUFFER = 10; /缓冲区长度 unsigned short ProductID = 0; /产品号 unsigned short ConsumeID = 0; /将被消耗的产品号 unsigned short in = 0; /产品进缓冲区时的缓冲区下标 unsigned short out = 0; /产品出缓冲区时的缓冲区下标 int g_bufferSIZE_OF_BUFFER; /缓冲区是个循环队列 bool g_continue = true;
9、/控制程序结束 HANDLE g_hMutex; /用于线程间的互斥 HANDLE g_hFullSemaphore; /当缓冲区满时迫使生产者等待 HANDLE g_hEmptySemaphore; /当缓冲区空时迫使消费者等待 DWORD WINAPI Producer(LPVOID); /生产者线程 DWORD WINAPI Consumer(LPVOID); /消费者线程 int main() /创建各个互斥信号 g_hMutex = CreateMutex(NULL,FALSE,NULL); g_hFullSemaphore = CreateSemaphore(NULL,SIZE_O
10、F_BUFFER-1,SIZE_OF_BUFFER-1,NULL); g_hEmptySemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER-1,NULL); /调整下面的数值,可以发现,当生产者个数多于消费者个数时, /生产速度快,生产者经常等待消费者;反之,消费者经常等待 const unsigned short PRODUCERS_COUNT = 3; /生产者的个数 const unsigned short CONSUMERS_COUNT = 1; /消费者的个数 /总的线程数 const unsigned short THREADS_COU
11、NT = PRODUCERS_COUNT+CONSUMERS_COUNT; HANDLE hThreadsPRODUCERS_COUNT; /各线程的 handle DWORD producerIDCONSUMERS_COUNT; /生产者线程的标识符 DWORD consumerIDTHREADS_COUNT; /消费者线程的标识符 /创建生产者线程 for (int i=0;iPRODUCERS_COUNT;+i) hThreadsi=CreateThread(NULL,0,Producer,NULL,0, if (hThreadsi=NULL) return -1; /创建消费者线程 f
12、or (int j=0; jCONSUMERS_COUNT; +j) hThreadsPRODUCERS_COUNT+j=CreateThread(NULL,0,Consumer,NULL,0, if (hThreadsj=NULL) return -1; while(g_continue) if(getchar() /按回车后终止程序运行 g_continue = false; return 0; /生产一个产品。简单模拟了一下,仅输出新产品的 ID 号 void Produce() std:cerr “Producing “ +ProductID “ . “; std:cerr “Succ
13、eed“ std:endl; /把新生产的产品放入缓冲区 void Append() std:cerr “Appending a product . “; g_bufferin = ProductID; in = (in+1)%SIZE_OF_BUFFER; std:cerr “Succeed“ std:endl; /输出缓冲区当前的状态 for (int i=0;iSIZE_OF_BUFFER;+i) std:cout i “: “ g_bufferi; if (i=in) std:cout “ - 生产“; if (i=out) std:cout “ - 消费“; std:cout std
14、:endl; /从缓冲区中取出一个产品 void Take() std:cerr “Taking a product . “; ConsumeID = g_bufferout; out = (out+1)%SIZE_OF_BUFFER; std:cerr “Succeed“ std:endl; /输出缓冲区当前的状态 for (int i=0;iSIZE_OF_BUFFER;+i) std:cout i “: “ g_bufferi; if (i=in) std:cout “ - 生产“; if (i=out) std:cout “ - 消费“; std:cout std:endl; /消耗一
15、个产品 void Consume() std:cerr “Consuming “ ConsumeID “ . “; std:cerr “Succeed“ std:endl; /生产者 DWORD WINAPI Producer(LPVOID lpPara) while(g_continue) WaitForSingleObject(g_hFullSemaphore,INFINITE); WaitForSingleObject(g_hMutex,INFINITE); Produce(); Append(); Sleep(1500); ReleaseMutex(g_hMutex); Release
16、Semaphore(g_hEmptySemaphore,1,NULL); return 0; /消费者 DWORD WINAPI Consumer(LPVOID lpPara) while(g_continue) WaitForSingleObject(g_hEmptySemaphore,INFINITE); WaitForSingleObject(g_hMutex,INFINITE); Take(); Consume(); Sleep(1500); ReleaseMutex(g_hMutex); ReleaseSemaphore(g_hFullSemaphore,1,NULL); retur
17、n 0; 4.测试用例,运行结果与运行情况分析:4.1 测试用例:在本次课程设计的测试中,缓冲区长度是取值为 10,生产者个数取值为 3,消费者个数取值为 1.4.2 运行结果与分析:在测试中调整生产者和消费者的数值,可以发现,当生产者个数多于消费者个数时,生产速度快,生产者经常等待消费者;反之,消费者经常等待。5.自我评价与总结:生产者-消费者问题是一个经典的进程同步问题,该问题最早由 Dijkstra 提出,用以演示他提出的信号量机制。在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生
18、产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。 而在本次课程设计中,为实现生产者和消费者问题,使用的是 C+语言,自我感觉比较好的是运用到了线程,将缓冲区作为一个循环队列,简单模拟了生产者和消费者对于缓冲区的输入输出,结果是比较成功的。其实在开始的时候,并没有想到运用线程,本来只想将生产者和消费者问题简单用两个动作“取产品”和“放产品”来实现,就是用人工控制的方式去实现:按 1 放产品,按 2 取产品,按 3 退出,在这样的情况下,不用去考虑产品编号什么的
19、,只用去关心缓冲区容量和产品个数的问题,也没有所谓的队列,只要有产品在缓冲区内,就可以执行取产品操作,而只要缓冲区内有空间,就可以执行放产品操作,但在自己动手编程的时候,出现了一系列的问题,发现自己想的太过简单了,仅仅靠那么点代码,那么简单的结构,貌似实现不了生产者和消费者的功能,所以后来在查阅和换位思考之后,决定引用线程的思想,勉勉强强做成功了,并且是自动的执行生产者和消费者的动作,在动态的执行过程中,体现生产者和消费者的关系。其实让用户自己动手控制生产者和消费者动作的方法也是可以的,只是我自己陷入了自己做的死胡同里,自己当时已经做不下去了,但事实是可以做出来的,在这里就不继续赘述了。完成本
20、题我觉得还可以用其他语言来做,可以用事件监听类的方法完成生产者和消费者的同步问题。总的来说,此次课程设计完成得还不错,对于老师所教授的信号量机制在解决进程互斥和同步问题的能力上都得到了训练,同时也让我更加熟悉掌握学习过的编程语言。本科生课程设计成绩评定表序号 评分项目 满分 实得分1 学习态度认真、遵守纪律 102 设计分析合理性 103设计方案正确性、可行性、创造性204 设计结果正确性 405 设计报告的规范性 106 设计验收 10总得分/等级评语:注:最终成绩以五级分制记。优(90-100 分) 、良(80-89 分) 、中(70-79 分) 、及格(60-69 分) 、60 分以下为不及格指导教师签名:20 年 月 日