logo资料库

读者写者问题操作系统课程设计报告.doc

第1页 / 共19页
第2页 / 共19页
第3页 / 共19页
第4页 / 共19页
第5页 / 共19页
第6页 / 共19页
第7页 / 共19页
第8页 / 共19页
资料共19页,剩余部分请下载后查看
操作系统课程设计报告 一、操作系统课程设计任务书 读者-写者问题实现 1 设计目的 通过实现经典的读者写者问题,巩固对线程及其同步机制的学习效果, 加深对相关基本概念的理解,并学习如何将基本原理和实际设计有机的结 合。 2 设计内容 在 Windows 2000/XP 环境下,使用多线程和信号量机制实现经典的读 者写者问题,每个线程代表一个读者或一个写者。每个线程按相应测试数 据文件的要求,进行读写操作。请用信号量机制分别实现读者优先和写者 优先的读者-写者问题。 读者-写者问题的读写操作限制: (1)写-写互斥,即不能有两个写者同时进行写操作 (2)读-写互斥,即不能同时有一个读者在读,同时却有一个写者在写 (3)读-读允许,即可以有二个以上的读者同时读 读者优先的附加限制:如果一个读者申请进行读操作时已有另一读者 正在进行读操作,则该读者可直接开始读操作。 写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者 在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开 始读操作。 运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始
读写操作和结束读写操作时分别显示一行提示信息,以确信所有处理都遵 守相应的读写操作限制。 3 测试数据文件格式 测试数据文件包括 n 行测试数据,分别描述创建的 n 个线程是读者还 是写者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字 段,各字段间用空格分隔。第一字段为一个正整数,表示线程序号。第二 字段表示相应线程角色,R 表示读者是,W 表示写者。第三字段为一个正 数,表示读写操作的开始时间。线程创建后,延时相应时间(单位为秒) 后发出对共享资源的读写申请。第四字段为一个正数,表示读写操作的持 续时间。当线程读写申请成功后,开始对共享资源的读写操作,该操作持 续相应时间后结束,并释放共享资源。下面是一个测试数据文件的例子: 1 R 3 5 2 W 4 5 3 R 5 2 4 R 6 5 5 W 5.1 3 4 相关 API 函数 CreateThread()在调用进程的地址空间上创建一个线程 ExitThread()用于结束当前线程 Sleep()可在指定的时间内挂起当前线程 CreateMutex()创建一个互斥对象,返回对象句柄 OpenMutex()打开并返回一个已存在的互斥对象句柄,用于后续访 问 ReleaseMutex()释放对互斥对象的占用,使之成为可用 WaitForSingleObject()可在指定的时间内等待指定对象为可用 状态 InitializeCriticalSection()初始化临界区对象 EnterCriticalSection()等待指定临界区对象的所有权
LeaveCriticalSection()释放指定临界区对象的所有权 二、设计思路 将所有的读者和所有的写者分别放进两个等待队列中,当 读允许时就让读者队列释放一个或多个读者,当写允许时,释 放第一个写者操作。 读者优先: 如果没有写者正在操作,则读者不需要等待,用一个整 型变量 readcount 记录当前的读者数目,用于确定是否释放写 者线程,(当 readcout=0 时,说明所有的读者都已经读完, 释 放 一 个 写 者 线 程 ), 每 个 读 者 开 始 读 之 前 都 要 修 改 readcount,为了互斥的实现对 readcount 的修改,需要一个互 斥对象 Mutex 来实现互斥。 另外,为了实现写-写互斥,需要一个临界区对象 write, 当写者发出写的请求时,必须先得到临界区对象的所有权。 通过这种方法,可以实现读写互斥,当 readcount=1 时,(即 第一个读者的到来时,),读者线程也必须申请临界区对象的 所有权. 当读者拥有临界区的所有权,写者都阻塞在临界区对象 write 上。当写者拥有临界区对象所有权时,第一个判断完 readcount==1 后,其余的读者由于等待对 readcount 的判断, 阻塞在 Mutex 上! 写者优先: 写者优先和读者优先有相同之处,不同的地方在:一旦 有一个写者到来时,应该尽快让写者进行写,如果有一个写 者在等待,则新到的读者操作不能读操作,为此添加一个整 型变量 writecount,记录写者的数目,当 writecount=0 时才可以 释放读者进行读操作! 为了实现对全局变量 writecount 的互 斥访问,设置了一个互斥对象 Mutex3。 为了实现写者优先,设置一个临界区对象 read,当有写者在写 或等待时,读者必须阻塞在临界区对象 read 上。 读者除了要一个全局变量 readcount 实现操作上的互斥 外,还需要一个互斥对象对阻塞在 read 这一个过程实现互 斥,这两个互斥对象分别为 mutex1 和 mutex2。
程序结构 主要代码进行分析: 1、临界区: CRITICAL_SECTION RP_Write; //临界区 CRITICAL_SECTION cs_Write; CRITICAL_SECTION cs_Read; 临界区(Critical Section)是一段独占对某些共享资源访问的代码, 在任意时刻只允许一个线程对共享资源进行访问。如果有多个线 程试图同时访问临界区,那么在有一个线程进入后其他所有试图 访问此临界区的线程将被挂起,并一直持续到进入临界区的线程 离开。临界区在被释放后,其他线程可以继续抢占,并以此达到 用原子方式操作共享资源的目的。 临界区在使用时以 CRITICAL_SECTION 结构对象保护共享 资源,并分别用 EnterCriticalSection()和 LeaveCriticalSection() 函数去标识和释放一个临界区。所用到的 CRITICAL_SECTION 结构对象必须经过 InitializeCriticalSection()的初始化后才能使 用,而且必须确保所有线程中的任何试图访问此共享资源的代码
都处在此临界区的保护之下。否则临界区将不会起到应有的作用, 共享资源依然有被破坏的可能。 2、定义线程结构: struct ThreadInfo { int Threadhao; char ThreadClass; double ThreadStartTime; double ThreadRunTime; }; 此结构用来存放线程的信息,四个成员变量依次表示线程序号、线 程类别、线程开始时间、线程读写持续时间。 3、互斥对象 创建互斥对象 CreateMutex(NULL,FALSE,"mutex_for_readcount"); 参数含义如下: NULL表示创建带有默认安全性的内核对象 FALSE表示该互斥对象没有被任何线程所拥有 mutex_for_readcount是为内核对象赋予名字。 释放互斥信号 ReleaseMutex(h_Mutex); 对资源具有访问权的线程不再需要访问此资源而要离开时,必 须通过ReleaseMutex()函数来释放其拥有的互斥对象 4、创建读者线程 CreateThread(NULL,0,\(LPTHREAD_START_ROUTINE)(R_ReaderTh read), \&thread_info[i],0,&thread_ID); 参数含义如下: NULL表示创建带有默认安全性的内核对象 0表示新读者线程拥有自己的堆栈,使用缺省大小:1MB。 (LPTHREAD_START_ROUTINE)(R_ReaderThread)表示新读 者线程执行的线程函数的地址
&thread_info[i]表示在线程启动执行时将该参数传递给 读者线程函数。 0表示读者线程创建后可以立即进行调度 &thread_ID表示CreateThread使用这个地址来存放系统 分配 5、等待函数 给新读者线程的I D WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1); 等待函数可使线程自愿进入等待状态,直到一个特定的内核对象 变为已通知状态为止 参数含义如下:n_thread表示线程数量。 h_Thread是指向线程对象句柄的数组的指针。 ture表示:在所有线程对象变为已通知状态之前,该函数将不 允许调用线程运行 参数 -1 告诉系统,调用线程愿意永远等待下去(无限时间 量),直到该进程终止运行。 三、运行结果 程序运行结果如下: 请选择要进行的操作: 1 (回车)
1 (回车) 请选择要进行的操作: 2 (回车)
四、设计总结 本次操作系统课程设计完成的是读者-写者问题,通过学习对 线程及其同步机制有了很的学习和掌握. 并认识到同步可以保证 在一个时间内只有一个线程对某个资源有控制权。共享资源包括 全局变量、公共数据成员或者句柄等。同步还可以使得有关联交 互作用的代码按一定的顺序执行。同时也掌握了实现线程同步的 对象有 Critical_section(关键段),Event(事件),Mutex(互 斥对象),Semaphores(信号量)并对这些对象理解如下: 对于关键段对象:首先,定义一个关键段对象;然后,初始化 该对象。初始化时把对象设置为 NOT_SINGALED,表示允许线程使 用资源:如果一段程序代码需要对某个资源进行同步保护,则这 是一段关键段代码。在进入该关键段代码前调用 EnterCriticalSection 函数,这样,其他线程都不能执行该段代 码,若它们试图执行就会被阻塞。完成关键段的执行之后,调用 LeaveCriticalSection 函数,其他的线程就可以继续执行该段代 码。如果该函数不被调用,则其他线程将无限期的等待。 对于互斥对象 首先,调用 CreateMutex 创建互斥对象;然后, 调用等待函数,可以的话利用关键资源;最后,调用 RealseMutex 释放互斥对象。互斥对象可以在进程间使用,但关键段对象只能 用于同一进程的线程之间。 对于等待函数:等待函数有:1.等待单个对象的(FOR SINGLE OBJECT): WaitForSingleObject,函数参数包括同步对象的句柄 和等待时间等。如果等待时间不限制(Infinite),则只有同步对 象获得信号才返回;如果等待时间为 0,则在测试了同步对象的 状态之后马上返回。2.等待多个对象的(FOR MULTIPLE OBJECTS) WaitForMultipleObjects,函数参数包括同步对象的句柄,等待时 间,是等待一个还是多个同步对象等等,如果等待时间不限制 (Infinite),则只有同步对象获得信号才返回;如果等待时间为 0,则在测试了同步对象的状态之后马上返回。 操作系统设计使我对 C++有了一个很好的复习和学习,对线程 的创建及同步问题及其同步对象都有了很好的理解。并能初步进
分享到:
收藏