实验一进程同步与互斥一、实验目的1.掌握基本的同步与互斥算法,理解生产者消费者模型。2.学习使用Windows2000/XP中基本的同步对象,掌握相关API的使用方法。3.了解Windows2000/XP中多线程的并发执行机制,实现进程的同步与互斥。二、实验内容及要求1.实验内容以生产者/消费者模型为依据,在Windows2000环境下创建一个控制台进程,在该进程中创建n个线程模拟生产者和消费者,实现进程(线程)的同步与互斥。2.实验要求学习并理解生产者/消费者模型及其同步/互斥规则;学习了解Windows同步对象及其特性;熟悉实验环境,掌握相关API的使用方法;设计程序,实现生产者/消费者进程(线程)的同步与互斥;提交实验报告。三、相关知识介绍1.同步对象同步对象是指Windows中用于实现同步与互斥的实体,包括信号量(Semaphore)、互斥量(Mutex)、临界区(CriticalSection)和事件(Events)等。本实验中使用到信号量、互斥量和临界区三个同步对象。同步对象的使用步骤:创建/初始化同步对象。请求同步对象,进入临界区(互斥量上锁)。释放同步对象(互斥量解锁)。这些对象在一个线程中创建,在其他线程中都可以使用,实现同步与互斥。2.相关API的功能及使用我们利用WindowsSDK提供的API编程实现实验题目要求,而VC中包含有WindowsSDK的所有工具和定义。要使用这些API,需要包含堆这些函数进行说明的SDK头文件——最常见的是Windows.h(特殊的API调用还需要包含其他头文件)。下面给出的是本实验使用到的API的功能和使用方法简单介绍。(1)CreateThread功能——创建一个在调用进程的地址空间中执行的线程格式HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParamiter,DWORDdwCreationFlags,LpdwordlpThread);参数说明lpThreadAttributes——指向一个LPSECURITY_ATTRIBUTES(新线程的安全性描述符)。dwStackSize——定义原始堆栈大小。lpStartAddress——指向使用LPTHRAED_START_ROUTINE类型定义的函数。lpParamiter——定义一个给进程传递参数的指针。dwCreationFlags——定义控制线程创建的附加标志。lpThread——保存线程标志符(32位)(2)CreateMutex功能——创建一个命名或匿名的互斥量对象格式HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes,BOOLbInitialOwner,LPCTSTRlpName);参数说明lpMutexAttributes——必须取值NULL。bInitialOwner——指示当前线程是否马上拥有该互斥量(即马上加锁)。lpName——互斥量名称。(3)CreateSemaphore功能——创建一个命名或匿名的信号量对象格式HANDLECreateSemaphore(LPSECURITY_ATTRIBUTESlpSemaphoreAttributes,LONGlInitialCount,LONGlMaximumCount,LPCTSTRlpName);参数说明lpSemaphoreAttributes——必须取值NULL。lInitialCount——信号量的初始值。该值大于0,但小于lMaximumCount指定的最大值。lMaximumCount——信号量的最大值。lpName——信号量名称。(4)WaitForSingleObject功能——使程序处于等待状态,直到信号量hHandle出现(即其值大于等于1)或超过规定的等待时间格式DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);参数说明hHandle——信号量指针。dwMilliseconds——等待的最长时间(INFINITE为无限等待)。(5)ReleaseSemaphore功能——对指定信号量加上一个指定大小的量。成功执行则返回非0值格式BOOLReleaseSemaphore(HANDLEhSemaphore,LONGlReleaseCount,LPLONGlppreviousCount);参数说明hSemaphore——信号量指针。lReleaseCount——信号量的增量。lppreviousCount——保存信号量当前值。(6)ReleaseMutex功能——打开互斥锁,即把互斥量加1。成功调用则返回0格式BOOLReleaseMutex(HANDLEhMutex);参数说明hMutex——互斥量指针。(7)InitializeCriticalSection功能——初始化临界区对象格式VOIDInitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);参数说明lpCriticalSection——指向临界区对象的指针。(8)EnterCriticalSection功能——等待指定临界区对象的所有权格式VOIDenterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);参数说明lpCriticalSection——指向临界区对象的指针。(9)LeaveCriticalSection功能——释放指定临界区对象的所有权格式VOIDLeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);参数说明lpCriticalSection——指向临界区对象的指针。四、实验示例(方法、步骤与例程)1.测试用例文件测试用例文件用于描述各线程的有关信息,该文件内容及格式如下:31P32P43C414P25C3124说明:第一行给出的是程序中设置的临界区个数;其余各行是各进程信息。每行中的数据之间用Tab键分隔。第一列(除第一行外):线程号。第二列:P——生产者,C——消费者。第三列:线程在生产和消费前的休眠时间,单位为秒。第四及以后各列:消费的产品所对应的生产者线程号。2.数据结构(1)用整型数组Buffer_Critical表示缓冲区。(2)用自定义结构ThreadInfo记录一条线程信息,多个线程对应一个ThreadInfo数组。(3)通过如下同步对象实现互斥:设一个互斥量h-mutex,实现生产者在查询和保留缓冲区的下一个空位置时进行互斥。设置h_Semaphore[MAX_THREAD_NUM]信号量数组表示相应产品已经生产,实现生产者与消费者之间的同步。同时,用表示空缓冲区树木的信号量empty_semephore指示是否存在空位置,实现类似的同步,以便开始下一个产品的生产。设置临界区对象数组PC_Critical[MAX_BUFFER_NUM]实现每个缓冲区上消费者之间的互斥。3.程序结构为了方便,程序结构用如下的文字予以描述。(1)主函数(2)初始化缓冲区、消费请求队列及部分同步对象(3)提取线程信息(4)完成线程相关同步对象的初始化(5)创建线程,模拟生产者和消费者(6)等待所有线程结束(7)程序结束(8)消费者(9)有无消费请求?有,则继续(10);无,则转(16)(10)此请求可满足?可满足,转(11);否,则阻塞,再转(10)(11)确定产品位置(12)此产品正被消费?是,则阻塞,再转(12);否,则转(13)(13)进入临界区(请求同一产品的消费者之间互斥)(14)消费产品,并判断是否应该释放产品所占缓冲区(15)退出临界区,转(9)(16)结束消费者线程(17)生产者(18)存在空缓冲区?有,则继续(19);无,则阻塞,再转(18)(19)另一生产者在写?否,则转(20);是,则阻塞,再转(19)(20)进入临界区(请求同一产品的生产者之间互斥)(21)在缓冲区中为本线程产品分配空间(22)退出临界区(23)写入产品到分配的缓冲区空间中(24)结束生产者线程4.示例程序//**************************R_WP1.CPP*******************************//#includewindows.h#includefstream.h#includestdio.h#includestring#includeconio.h//本程序允许的最大临界区数#defineMAX_BUFFER_NUM10//秒到微秒的乘法因子#defineINTE_PER_SEC1000//生产和消费进程总数#defineMAX_THREAD_NUM64//定义记录在测试文件中的线程参数数据结构structThreadInfo{intserial;charentity;doubledelay;intthread_request[MAX_THREAD_NUM];intn_request;};//全局变量定义//临界区对象的声明,用于管理缓冲区的互斥访问CRITICAL_SECTIONPC_Critical[MAX_BUFFER_NUM];intBuffer_Critical[MAX_BUFFER_NUM];//缓冲区声明HANDLEh_Thread[MAX_THREAD_NUM];//存储线程句柄的数组ThreadInfoThread_info[MAX_THREAD_NUM];//线程信息数组HANDLEempty_semephore;//信号量HANDLEh_mutex;//互斥量DWORDn_Thread=0;//实际线程数目DWORDn_Buffer_or_Critical;//实际缓冲区/临界区数目HANDLEh_semephore[MAX_THREAD_NUM];//生产者允许消费的信号量//生产、消费及辅助函数的声明voidProduce(void*p);voidConsume(void*p);boolIfInOtherRequest(int);intFindProducePosition();intFindBufferPosition(int);intmain(void){DWORDwait_for_all;ifstreaminFile;//初始化缓冲区for(inti=0;iMAX_BUFFER_NUM;i++)Buffer_Critical[i]=-1;//初始化各线程的请求队列for(intj=0;jMAX_THREAD_NUM;j++){for(intk=0;jMAX_THREAD_NUM;k++)Threa_Info[j].thread_request[k]=1;Thread_Info[j].n_request=0;}//初始化临界区对象for(inti=0;iMAX_BUFFER_NUM;i++)InitializeCriticalSection(&PC_Critical[i]);//打开输入文件,提取线程信息inFile.open(“test.txt”);//从文件中获得实际缓冲区数目inFilen_Buffer_or_Critical;inFile.get();printf(“输入文件是:\n”);//回显获得的缓冲区数目printf(“%d\n”,(int)n_Buffer_or_Critical);//提取各线程信息到相应的数据结构中while(inFile){inFileThread_Info[n_Thread].serial;inFileThread_I