当前位置:文档之家› 生产者与消费者实验报告

生产者与消费者实验报告

一、实验目的利用Windows提供的API函数,编写程序,解决生产者与消费者问题,实现进程的互斥与同步。

二、实验内容本实验要求设计在同一个进程地址空间内执行的两个线程。

生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。

消费者线程从缓冲区中获得物品,然后释放缓冲区。

生产者线程生产物品时,若无空缓冲区可用,生产者线程必须等待消费者线程释放出一个空缓冲区;消费者线程消费物品时,若缓冲区为空,消费者线程将被阻塞,直到新的物品被生产出来。

生产者和消费者使用N个不同的缓冲区(N 为一个确定的数值,例如N=32)。

需要使用如下信号量:一个互斥信号量,用以阻止生产者线程和消费者线程同时操作缓冲区列表;一个信号量,当生产者线程生产出一个物品时可以用它向消费者线程发出信号;一个信号量,消费者线程释放出一个空缓冲区时可以用它向生产者线程发出信号;三、实验步骤1.创建信号量根据题目的要求,首先创建信号量。

本次实验共需使用三个信号量:一个用以阻止生产者线程和消费者线程同时操作缓冲区列表的互斥信号量,一个当生产者线程生产出一个物品时可以用它向消费者线程发出信号的信号量以及一个消费者线程释放出一个空缓冲区时可以用它向生产者线程发出信号的信号量。

使用Windows提供的CreateSemaphore函数和CreateMutex创建一个新的信号量。

CreateSemaphore函数原型:HANDLE CreateSemaphore{LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCTSTR lpName};如执行成功,返回信号量对象的句柄;零表示出错。

本程序所使用的该函数中各参数的解释:1.lpSemaphoreAttributesSECURITY_ATTRIBUTES,指定一个SECURITY_ATTRIBUTES结构,或传递零值,表示采用不允许继承的默认描述符。

该参数定义了信号量的安全特性。

2.lInitialCount设置信号量的初始计数。

可设置零到lMaximumCount之间的一个值。

3.lMaximumCount设置信号量的最大计数。

4.lpName指定信号量对象的名称。

可赋值为NULL。

CreateMutex函数原型:HANDLE CreateMutex{LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName};找出当前系统是否已经存在指定进程的实例。

如果没有则创建一个互斥体。

CreateMutex函数可用来创建一个有名或无名的互斥量对象。

本程序所使用的该函数中各参数的解释:1.lpMutexAttributes指定一个SECURITY_ATTRIBUTES结构,或传递零值表示使用不允许继承的默认描述符。

2.bInitialOwner如创建进程希望立即拥有互斥体,则设为TRUE。

一个互斥体同时只能由一个线程拥有。

3.lpName String指定互斥体对象的名字。

2.创建线程使用了Windows提供的CreateThread函数来创建一个在调用进程的地址空间中执行的线程。

函数原型HANDL CreateThread{LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadld};CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:1.在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回;2.把线程退出码置为STILL_ACTIVE,把线程挂起计数置1;3.分配context结构,分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD;4.lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数;5.把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread 函数。

本程序所使用的函数中各参数的解释:1.pThreadAttributes指向SECURITY_ATTRIBUTES型态的结构的指针。

初始化为NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle 成员初始化为TRUE。

2.dwStackSize设置初始栈的大小,以字节为单位,如果初始值设为为0,那么默认将使用与调用该函数的线程相同的栈空间大小。

任何情况下,Windows根据需要动态延长堆栈的大小。

3.lpStartAddress指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:DWORD WINAPI 函数名 (LPVOID lpParam),格式不正确将无法调用成功。

4.lpParameter向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。

5.dwCreationFlags线程标志,可取值如下:(1)CREATE_SUSPENDED(0x00000004):创建一个挂起的线程,(2)0:表示创建后立即激活。

(3)STACK_SIZE_PARAM_IS_A_RESERVATION(0x00010000):dwStackSize参数指定初始的保留堆栈的大小,否则,dwStackSize指定提交的大小。

6.lpThreadId保存新线程的id。

当函数成功,返回线程句柄;函数失败返回false;若不想返回线程ID,设置值为NULL。

3.信号量的变化在程序的运行过程中必须动态改变第一步中设置的三个信号量的参数值。

使用的是Windows提供的WaitForSingleObject函数、ReleaseMutex函数和ReleaseSemaphore函数WaitForSingleObject函数原型DWORD WaitForSingleObject{HANDLE hHandle,DWORD dwMilliseconds};本程序所使用的函数中各参数的解释:1.hHandle对象句柄,可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。

2.dwMilliseconds定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。

如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。

如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。

ReleaseMutex函数原型BOOL WIANPI ReleaseMutex{HANDLE hMutex};ReleaseMutex函数的功能是释放互斥对象的控制权。

返回TRUE表示成功,FALSE表示失败。

本程序所使用的函数中参数的解释:HANDLE hMutexHANDLE,制定一个互斥体的句柄。

ReleaseSemaphore函数原型BOOL ReleaseSemaphore{HANDLE hSemaphore,LONG lReleaseCount,LPLONG lpPreviousCount};ReleaseSemaphore函数用于对指定的信号量增加指定的值。

本程序所使用的函数中参数的解释:1.hSemaphore所要操作的信号量对象的句柄,这个句柄是CreateSemaphore或者OpenSemaphore函数的返回值且必须有SEMAPHORE_MODIFY_STATE的权限。

2.lReleaseCount信号量对象在当前基础上所要增加的值(必须大于0)。

如果信号量加上这个值会导致信号量的当前值大于信号量创建时指定的最大值,则信号量的当前值不变,同时这个函数返回FALSE。

3.lpPreviousCount指向返回信号量上次值的变量的指针,如果不需要信号量上次的值,那么这个参数可以设置为NULL。

四、系统截图图1 初始界面图2 生产者生产物品并显示缓冲区状态图3 消费者消耗物品并显示缓冲区状态五、遇到的问题及思考1、因为本程序允许有多个生产者和消费者存在,这就导致了一开始的结果中分不清到底是哪个生产者或消费者在工作,因此决定加上编号,但由于当时对CreateThread函数还不是很了解所以不知道如何传递参数,最后通过参考网上资料后得知CreateThread中的第四个参数lpParameter向线程函数传递的数值,在这里编号即可。

2、一开始无论如何修改程序(即使设置较长的时间断点),程序总会以“一个生产者一个消费者”这样的顺序运行。

这一点让我十分的疑惑,因为当我去掉消费者线程时,程序居然完成一个生产者的过程后停止。

经过思考后我发现问题出现在语句WaitForSingleObject(m_S_Empty, WaitTime) == WAIT_OBJECT_0,网上查阅资料后得知执行该语句则只有第一个线程被通知,因此事实上也只有完成了第一个生产者线程。

最后将该条语句换为WaitForSingleObject(m_S_Empty, WaitTime),这样所有的线程均被通知。

3、在程序运行时发现几个生产者线程提示信息缠在一起,经考虑后认定问题出在WaitTime的赋值上,需要较大的数值才能保证两个程序分开。

相关主题