操作系统试验--读者-写者问题周俊霞20112113202011211307班进程同步一.实习要求:本课程实验内容引自《Windows内核实验教程》(陈向群、林斌等编著,机械工业出版社2002.9)。在Windows环境下,创建一个包含n个线程的控制进程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件的要求,进行读写操作。请用信号量机制分别实现读者优先和写者优先的读者-写者问题。1.读者-写者问题的读写操作限制:1)写-写互斥;2)读-写互斥;3)读-读允许;2.读者优先的附加限制:如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作。写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息,以确信所有处理都遵守相应的读写操作限制。二.测试数据文件格式测试数据文件包括n行测试数据,分别描述创建的n个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字段,各字段间用空格分隔。第一字段为一个正整数,表示线程序号。第一字段表示相应线程角色,R表示读者是,W表示写者。第二字段为一个正数,表示读写操作的开始时间。线程创建后,延时相应时间(单位为秒)后发出对共享资源的读写申请。第三字段为一个正数,表示读写操作的持续时间。当线程读写申请成功后,开始对共享资源的读写操作,该操作持续相应时间后结束,并释放共享资源。下面是一个测试数据文件的例子:1R352W453R524R655W5.13三、与实验相关的API介绍在本实验中可能涉及的API有:线程控制:CreateThread完成线程创建,在调用进程的地址空间上创建一个线程,以执行指定的函数;它的返回值为所创建线程的句柄。HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,//SDDWORDdwStackSize,//initialstacksizeLPTHREAD_START_ROUTINElpStartAddress,//threadfunctionLPVOIDlpParameter,//threadargumentDWORDdwCreationFlags,//creationoptionLPDWORDlpThreadId//threadidentifier);ExitThread用于结束当前线程。VOIDExitThread(DWORDdwExitCode//exitcodeforthisthread);Sleep可在指定的时间内挂起当前线程。VOIDSleep(DWORDdwMilliseconds//sleeptime);信号量控制:CreateMutex创建一个互斥对象,返回对象句柄;HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes,//SDBOOLbInitialOwner,//initialownerLPCTSTRlpName//objectname);OpenMutex打开并返回一个已存在的互斥对象句柄,用于后续访问;HANDLEOpenMutex(DWORDdwDesiredAccess,//accessBOOLbInheritHandle,//inheritanceoptionLPCTSTRlpName//objectname);ReleaseMutex释放对互斥对象的占用,使之成为可用。BOOLReleaseMutex(HANDLEhMutex//handletomutex);WaitForSingleObject可在指定的时间内等待指定对象为可用状态;DWORDWaitForSingleObject(HANDLEhHandle,//handletoobjectDWORDdwMilliseconds//time-outinterval);程序由入口函数main开始,打印出菜单,选择1则选择读者优先,调用ReaderPriority(thread.dat)函数;选择2则选择写者优先,调用WriterPriority(thread.dat)函数;选择3则退出。读者优先:ReaderPriority函数首先读取目标文件Thread.dat,为每一行请求创建一个线程,其中读请求创建读者线程,调用RP_ReaderThread函数,写请求创建写者线程,调用RP_WriterThread函数。RP_ReaderThread函数的实现如下:wait(mutex);read_count++;if(read_count==1)wait(&RP_Write);signal(mutex);读临界区……wait(mutex);read_count--;if(read_count==0)signal(&RP_Write);signal(mutex);RP_WriterThread函数的实现如下:wait(&RP_Write);写临界区……signal(&RP_Write);写者优先:WriterPriority函数首先读取目标文件Thread.dat,为每一行请求创建一个线程,其中读请求创建读者线程,调用WP_ReaderThread函数,写请求创建写者线程,调用WP_WriterThread函数。WP_ReaderThread函数实现如下:wait(mutex1);wait(&cs_Read);wait(mutex2);read_count++;if(read_count==1)wait(&cs_Write);signal(mutex2);signal(&cs_Read);signal(mutex1);读临界区……wait(mutex2);read_count--;if(read_count==0)signal(&cs_Write);signal(mutex2);WP_WriterThread函数实现如下:wait(mutex3);write_count++;if(write_count==1)wait(&cs_Read);signal(mutex3);wait(&cs_Write);写临界区……signal(&cs_Write);wait(mutex3);write_count--;if(write_count==0)signal(&cs_Read);signal(mutex3);在本实验中可能涉及的API有:线程控制:CreateThread完成线程创建,在调用进程的地址空间上创建一个线程,以执行指定的函数;它的返回值为所创建线程的句柄。HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,//initialstacksizeLPTHREAD_START_ROUTINElpStartAddress,//threadfunctionLPVOIDlpParameter,//threadargumentDWORDdwCreationFlags,//creationoptionLPDWORDlpThreadId//threadidentifier);ExitThread用于结束当前线程。VOIDExitThread(DWORDdwExitCode//exitcodeforthisthread);Sleep可在指定的时间内挂起当前线程。VOIDSleep(DWORDdwMilliseconds//sleeptime);信号量控制:CreateMutex创建一个互斥对象,返回对象句柄;HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes,BOOLbInitialOwner,//initialownerLPCTSTRlpName//objectname);OpenMutex打开并返回一个已存在的互斥对象句柄,用于后续访问;HANDLEOpenMutex(DWORDdwDesiredAccess,//accessBOOLbInheritHandle,//inheritanceoptionLPCTSTRlpName//objectname);ReleaseMutex相当于V操作,释放对互斥对象的占用,使之成为可用。BOOLReleaseMutex(HANDLEhMutex//handletomutex);WaitForSingleObject相当于P操作,可在指定的时间内等待指定对象为可用状态;DWORDWaitForSingleObject(HANDLEhHandle,//handletoobjectDWORDdwMilliseconds//time-outinterval);EnterCriticalSectionLeaveCriticalSection四、实验实例:1R352W453R524R655W5.131.读优先:只要有一个读者正在读,那么后续的读者都能立即读,不管有多少写者在等待。可能导致写者饥饿。1.读者1)写者写时,不可读2)有别的读者正在读,可读2.写者1)有读者正在读,不可写2)有写者正在写,不可写3)无读者正在读,无写者正在写,可写运行结果正确:1R352W453R524R655W5.13读者优先:依次为:读者1发送读请求;读者1开始读操作;写者2发送写请求;读者3发送读请求;读者3开始读操作;写者5发送写请求;读者4发送读请求;读者4开始读操作;读者3完成了读操作;读者1完成了读操作;读者4完成了读操作;写者2开始写操作;写者2完成写操作;写者5开始写操作;写者5完成写操作;2.写优先:1R352W453R524R655W5.13当新的写者希望写时,不允许该写者后续的读者访问数据区,但必须保证之前的读者读完。1.读者特点1)有写者正在写或者等待写,须等到没有写者才能读2)没有写者,可以读2.写者特点1)写者与写者互斥。当其它写者正在写时,其它写者不能写。2)写者与读者互斥。之前只有读者在读,当写者出现时,必须等到之前的读者都读完才能写。这尊重了之前读者的意愿。3)写者可以有条件地插读者的队。当前有写者正写,有读者在等,这时来了新写者,新写者可以在那些读者之前执行。3.写者实现事实上,这个写者优先并没有成功实现。。。只是学到了一些知识。。。。。首先,写者的代码应该是这样一种形式,才能保证同一时刻只有一个写者修改数据:[cpp]viewplaincopywhile(1){pthread_mutex_lock(&writeLock);{//临界区,限制只有一个写者修改数据write();}pthread_mutex_unlock(&writeLock);}考虑到写者对读者的影响是:当任何写者想写时,读者都必须被阻塞;并且,写者阻塞了读者并停止阻塞之前,后续的任何写者都会优先于读者执行。这就如同有一个写者队列,当第一个写者入队时,读者完全被阻塞,直到最后一个写者离开队列。据此,可以用writerCnt来统计写者的数量,而用互斥量accessWriterCnt来互斥各线程对writerCnt的访问。代码作如下调整:[cpp]viewplaincopywhile(1){pthread_mutex_lock(&accessWriterCnt);{//临界区,希望修改writerCnt,独占writerCntwriterCnt++;if(writerCnt==1){//读者与写者互斥;使用readerLock来描述;pthread_mutex_lock(&readerLock);}}pthread_mutex_unlock(&accessWriterCnt);pthread_mutex_lock(&wr