读者写者实验报告

时间:2019-05-14 10:49:21下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《读者写者实验报告》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《读者写者实验报告》。

第一篇:读者写者实验报告

操作系统原理 实验报告

实验名称:

姓 名:学 号:班 级:指导老师:

操作系统 XXX xxxxxxxxxx xxx xxx

一、实验内容

在Windows2000环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件(后面有介绍)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。读者-写者问题的读写操作限制(包括读者优先和写者优先): 1)写-写互斥,即不能有两个写者同时进行写操作。

2)读-写互斥,即不能同时有一个线程在读,而另一个线程在写。,3)读-读允许,即可以有一个或多个读者在读。

读者优先的附加限制:如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。

写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写操作限制。

二、实验目的

在Windows2000环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件(后面有介绍)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。

三、实验原理 1).读者优先

读者优先指的是除非有写者在写文件,否则读者不需要等待。所以可以用一个整型变量read_count记录当前的读者数目,用于确定是否需要释放正在等待的写者线程(当read_count=O时,表明所有的读者读完,需要释放写者等待队列中的一个写者)。每一个读者开始读文件时,必须修改read_count变量。因此需要一个互斥对象mutex来实现对全局变量read_count修改时的互斥。

另外,为了实现写-写互斥,需要增加一个临界区对象write。当写者发出写请求时,必须申请临界区对象的所有权。通过这种方法,也可以实现读-写互斥,当read_count=l时(即第一个读者到来时),读者线程也必须申请临界区对象的所有权。

当读者拥有临界区的所有权时,写者阻塞在临界区对象write上。当写者拥有临界区的所有权时,第一个读者判断完 “read_count==1”后阻塞在write上,其余的读者由于等待对read_count的判断,阻塞在mutex上。

2).写者优先

写者优先与读者优先类似;不同之处在于一旦一个写者到来,它应该尽快对文件进行写操作,如果有一个写者在等待,则新到来的读者不允许进行读操作。为此应当添加一个整型变量write_count,用于记录正在等待的写者的数目,当write_count=O时,才可以释放等待的读者线程队列。

为了对全局变量write_count实现互斥,必须增加一个互斥对象mutex3。为了实现写者优先,应当添加一个临界区对象read,当有写者在写文件或等 待时,读者必须阻塞在read上。·

读者线程除了要对全局变量read_count实现操作上的互斥外,还必须有一个互斥对象对阻塞,read这一过程实现互斥。这两个互斥对象分别命名为mutex1和mutex2。

四、实验过程

在Windows2000环境下,创建一个控制台进程。用VC++实现。

读者优先指的是除非有写者在写文件,否则读者不需要等待。所以可以用一个整型变量read_count记录当前的读者数目,用于确定是否需要释放正在等待的写者线程(当read_count=O时,表明所有的读者读完,需要释放写者等待队列中的一个写者)。每一个读者开始读文件时,必须修改read_count变量。因此需要一个互斥对象mutex来实现对全局变量read_count修改时的互斥。

另外,为了实现写-写互斥,需要增加一个临界区对象write。当写者发出写请求时,必须申请临界区对象的所有权。通过这种方法,也可以实现读-写互斥,当read_count=l时(即第一个读者到来时),读者线程也必须申请临界区对象的所有权。

当读者拥有临界区的所有权时,写者阻塞在临界区对象write上。当写者拥有临界区的所有权时,第一个读者判断完 “read_count==1”后阻塞在write上,其余的读者由于等待对read_count的判断,阻塞在mutex上。

写者优先与读者优先类似;不同之处在于一旦一个写者到来,它应该尽快对文件进行写操作,如果有一个写者在等待,则新到来的读者不允许进行读操作。为此应当添加一个整型变量write_count,用于记录正在等待的写者的数目,当write_count=O时,才可以释放等待的读者线程队列。

为了对全局变量write_count实现互斥,必须增加一个互斥对象mutex3。为了实现写者优先,应当添加一个临界区对象read,当有写者在写文件或等 待时,读者必须阻塞在read上。·

读者线程除了要对全局变量read_count实现操作上的互斥外,还必须有一个互斥对象对阻塞,read这一过程实现互斥。这两个互斥对象分别命名为mutex1和mutex2。结构:

头文件 定义全局变量

RP_ReaderThread()读者优先---读者线程 RP_WriterThread()读者优先---写者线程 ReaderPriority()读者优先处理函数 WP_ReaderThrea()写者优先---读者线程 WP_WriterThread()写者优先---写者线程 WriterPriority()写者优先处理函数

主函数:包含对dat文件的读取以及函数的调用。代码:附录

五、实验结果

界面:

测试数据: 2 W 4 5 3 R 5 2 4 R 6 5 5 W 5.1 3 结果 读者优先

写者优先

六、实验心得体会

通过对读者写者问题的编程,对线程有了更深的了解,希望在后面的学习中懂得更多。

七、参考文献

老师提供的资料,以及互联网查阅。

八、附录

#include “windows.h” #include #include #include #include #include #include #include “winbase.h”

#define READER 'R' // 读者 #define WRITER 'W' // 写者

#define INTE_PER_SEC 1000 // 每秒时钟中断的数目 #define MAX_THREAD_NUM 64 // 最大线程数 int nReaderCnt = 0;// 读者计数 int nWriterCnt = 0;// 写者计数

HANDLE hWrite = ::CreateSemaphore(NULL, 1, 1, NULL);// 写开始信号

HANDLE hRead = ::CreateSemaphore(NULL, 1, 1, NULL);// 读开始信号

HANDLE hRCMutex = ::CreateMutex(NULL, FALSE, NULL);HANDLE hWCMutex = ::CreateMutex(NULL, FALSE, NULL);HANDLE hReadMutex = ::CreateMutex(NULL, FALSE, NULL);// 从测试数据文件中获取的线程信息 struct ThreadInfo { ThreadInfo(){ nSerialNo = 0;cType = '^';dDelayTime = 0.0;dOpeTime = 0.0;} int nSerialNo;// 线程序号 char cType;// 线程类别 double dDelayTime;// 线程延迟时间 double dOpeTime;// 线程读写操作时间 };// 读者优先---读者线程 // P:读者线程信息

void RP_ReaderThread(void *p){

int nSerialNo =((ThreadInfo*)(p))->nSerialNo;//从文件中读取 线程序号 DWORD dwReadTime

=

(DWORD)(((ThreadInfo*)(p))->dOpeTime

* INTE_PER_SEC);DWORD dwDelay

=

(DWORD)(((ThreadInfo*)(p))->dDelayTime

* INTE_PER_SEC);Sleep(dwDelay);printf(“Reader thread %d sents the reading require.n”,nSerialNo);WaitForSingleObject(hRCMutex, INFINITE);nReaderCnt++;if(nReaderCnt == 1){ WaitForSingleObject(hWrite, INFINITE);} ReleaseMutex(hRCMutex);printf(“Reader thread %d begins to read file.n”, nSerialNo);Sleep(dwReadTime);printf(“Reader thread %d finished reading file.n”, nSerialNo);WaitForSingleObject(hRCMutex, INFINITE);nReaderCnt--;if(nReaderCnt == 0){ ReleaseSemaphore(hWrite, 1, NULL);} ReleaseMutex(hRCMutex);} // 读者优先---写者线程 // P:写者线程信息

void RP_WriterThread(void *p){

int nSerialNo =((ThreadInfo*)(p))->nSerialNo;// 从参数中获得信息

DWORD dwWriteTime =(DWORD)(((ThreadInfo*)(p))->dOpeTime * INTE_PER_SEC);DWORD dwDelay

=

(DWORD)(((ThreadInfo*)(p))->dDelayTime

* INTE_PER_SEC);Sleep(dwDelay);printf(“Write thread %d sents the writing require.n”,nSerialNo);WaitForSingleObject(hWrite, INFINITE);printf(“Writer thread %d begins to write to the file.n”, nSerialNo);Sleep(dwWriteTime);printf(“Write thread %d finished writing to the file.n”, nSerialNo);

ReleaseSemaphore(hWrite, 1, NULL);} // 读者优先处理函数 // file:文件名

void ReaderPriority(char *file){ int nThreadCnt = 0;DWORD dwThreadID = 0;nReaderCnt = 0;// 初始化读写者计数 HANDLE hThreads[MAX_THREAD_NUM];ThreadInfo oThreadInfo[MAX_THREAD_NUM];ifstream inFile;inFile.open(file);printf(“Reader Priority:nn”);while(inFile){ // 读入每一个读者,写者的信息

inFile>>oThreadInfo[nThreadCnt].nSerialNo;inFile>>oThreadInfo[nThreadCnt].cType;inFile>>oThreadInfo[nThreadCnt].dDelayTime;inFile>>oThreadInfo[nThreadCnt].dOpeTime;if('^'!= oThreadInfo[nThreadCnt].cType){ nThreadCnt++;} inFile.get();} // 创建线程

for(int i = 0;i< nThreadCnt;i++){ if((oThreadInfo[i].cType==READER)||(oThreadInfo[i].cType == 'r')){ hThreads[i]

=

CreateThread(NULL,(LPTHREAD_START_ROUTINE)(RP_ReaderThread), // 创建读者进程 &oThreadInfo[i], CREATE_SUSPENDED, &dwThreadID);} else { hThreads[i]

=

CreateThread(0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),// 创建写线程 &oThreadInfo[i], CREATE_SUSPENDED, &dwThreadID);} }

for(i = 0;i< nThreadCnt;i++){

0,NULL, ResumeThread(hThreads[i]);} WaitForMultipleObjects(nThreadCnt, hThreads, TRUE, INFINITE);printf(“All reader and writer have finished operating.n”);} // 写者优先---读者线程 // P:读者线程信息

void WP_ReaderThread(void *p){ int nSerialNo =((ThreadInfo*)(p))->nSerialNo;// 从参数中得到信息 DWORD dwReadTime

=

(DWORD)(((ThreadInfo*)(p))->dOpeTime

* INTE_PER_SEC);DWORD dwDelay

=

(DWORD)(((ThreadInfo*)(p))->dDelayTime

* INTE_PER_SEC);Sleep(dwDelay);printf(“Reader thread %d sents the reading require.n”,nSerialNo);WaitForSingleObject(hReadMutex, INFINITE);WaitForSingleObject(hRead, INFINITE);WaitForSingleObject(hRCMutex, INFINITE);nReaderCnt++;if(nReaderCnt==1){ WaitForSingleObject(hWrite, INFINITE);} ReleaseMutex(hRCMutex);ReleaseSemaphore(hRead, 1, NULL);ReleaseMutex(hReadMutex);printf(“Reader thread %d begins to read file.n”, nSerialNo);Sleep(dwReadTime);printf(“Reader thread %d finished reading file.n”, nSerialNo);WaitForSingleObject(hRCMutex, INFINITE);nReaderCnt--;if(nReaderCnt == 0){ ReleaseSemaphore(hWrite, 1, NULL);} ReleaseMutex(hRCMutex);} // 写者优先---写者线程 // P:写者线程信息

void WP_WriterThread(void *p){ int nSerialNo =((ThreadInfo*)(p))->nSerialNo;DWORD dwWriteTime =(DWORD)(((ThreadInfo*)(p))->dOpeTime * INTE_PER_SEC);DWORD dwDelay

=

(DWORD)(((ThreadInfo*)(p))->dDelayTime

* INTE_PER_SEC);Sleep(dwDelay);printf(“Writer thread %d sents the writing require.n”,nSerialNo);WaitForSingleObject(hWCMutex, INFINITE);nWriterCnt++;if(nWriterCnt == 1){ WaitForSingleObject(hRead, INFINITE);} ReleaseMutex(hWCMutex);WaitForSingleObject(hWrite, INFINITE);printf(“Writer thread %d begins to write to the file.n”, nSerialNo);Sleep(dwWriteTime);printf(“Writer thread %d finished writing to the file.n”, nSerialNo);ReleaseSemaphore(hWrite, 1, NULL);WaitForSingleObject(hWCMutex, INFINITE);nWriterCnt--;if(nWriterCnt == 0){ ReleaseSemaphore(hRead, 1, NULL);} ReleaseMutex(hWCMutex);} // 写者优先处理函数 // file:文件名

void WriterPriority(char * file){ int nThreadCnt = 0;DWORD dwThreadID;

HANDLE hThreads[MAX_THREAD_NUM];ThreadInfo oThreadInfo[MAX_THREAD_NUM];

nReaderCnt=0;nWriterCnt=0;

ifstream inFile;inFile.open(file);printf(“Writer priority:nn”);while(inFile){ inFile>>oThreadInfo[nThreadCnt].nSerialNo;inFile>>oThreadInfo[nThreadCnt].cType;inFile>>oThreadInfo[nThreadCnt].dDelayTime;inFile>>oThreadInfo[nThreadCnt].dOpeTime;if('^'!= oThreadInfo[nThreadCnt].cType){ nThreadCnt++;} inFile.get();}

for(int i = 0;i < nThreadCnt;i++)// 创建线程 { if((oThreadInfo[i].cType == READER)||(oThreadInfo[i].cType == 'r')){ hThreads[i]

=

CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WP_ReaderThread), //创建读者进程 &oThreadInfo[i], CREATE_SUSPENDED, &dwThreadID);} else { hThreads[i]

=

CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WP_WriterThread), &oThreadInfo[i],CREATE_SUSPENDED,&dwThreadID);} } for(i = 0;i< nThreadCnt;i++){ ResumeThread(hThreads[i]);} WaitForMultipleObjects(nThreadCnt, hThreads, TRUE, INFINITE);

printf(“All reader and writer have finished operating.n”);} int main(int argc,char *argv[]){ char ch;while(true){ printf(“*************************************n”);printf(“ 1.Reader Priorityn”);printf(“ 2.Writer Priorityn”);printf(“ 3.Exit to Windowsn”);printf(“*************************************n”);printf(“Enter your choice1,2,3: ”);do{ ch=(char)_getch();}while(ch!='1'&&ch!='2'&&ch!='3');system(“cls”);if(ch=='3')return 0;else if(ch=='1')ReaderPriority(“thread.dat”);else WriterPriority(“thread.dat”);printf(“nPress Any Key to Coutinue:”);_getch();system(“cls”);} return 0;}

第二篇:操作系统读者写者实验报告

目录

一 设计概述 ……………………………………………………2

二 设计目的与内容 ……………………………………………3

三 设计分析 ……………………………………………………4

四 程序实现 ……………………………………………………5

五 程序调试 ……………………………………………………6

六 结果分析和讨论 ……………………………………………6

七 心得体会 ……………………………………………………7

八 源代码 ………………………………………………………7

一 设计概述

所谓读者写者问题,是指保证一个writer进程必须与其他进程互斥地访问共享对象的同步问题。

读者写者问题可以这样的描述,有一群写者和一群读者,写者在写同一本书,读者也在读这本书,多个读者可以同时读这本书,但是,只能有一个写者在写书,并且,读者必写者优先,也就是说,读者和写者同时提出请求时,读者优先。当读者提出请求时需要有一个互斥操作,另外,需要有一个信号量S来当前是否可操作。

信号量机制是支持多道程序的并发操作系统设计中解决资源共享时进程间的同步与互斥的重要机制,而读者写者问题则是这一机制的一个经典范例。

与记录型信号量解决读者—写者问题不同,信号量机制它增加了一个限制,即最多允许RN个读者同时读。为此,又引入了一个信号量L,并赋予初值为RN,通过执行wait(L,1,1)操作,来控制读者的数目,每当有一个读者进入时,就要执行wait(L,1,1)操作,使L的值减1。当有RN个读者进入读后,L便减为0,第RN+1 个读者要进入读时,必然会因wait(L,1,1)操作失败而堵塞。对利用信号量来解决读者—写者问题的描述如下: Var RN integer;L,mx:semaphore: =RN,1; Begin Parbegin Reader :begin Repeat Swait(L,1,1);Swait(mx,1,0);.Perform reader operation;Ssignal(L,1);Until false;End Writer :begin Repeat Swait(mx ,1,1,l,RN,0);Perform writer operation;Ssignal(mx,1);Until false;End Parend End 其中,Swait(mx,1,0)语句起着开关作用,只要无Writer进程进入些,mx=1,reader进程就都可以进入读。但是要一旦有Writer进程进入写时,其MX=0,则任何reader进程就都无法进入读。Swait(mx ,1,1,l,RN,0)语句表示仅当既无Write进程在写(mx=1),又无reader进程在读(L=RN)时,writer进程才能进入临界区写。

本设计方案就是通过利用记录型信号量对读者写者问题的解决过程进行模拟演示,形象地阐述记录型信号量机制的工作原理。

二 设计目的与内容

一 实验目的

l.用信号量来实现读者写者问题。

2.理解和运用信号量、PV原语、进程间的同步互斥关系等基本知识。

二、二 实验内容

读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:

(1)任意多个读进程可以同时读这个文件;(2)一次只有一个写进程可以往文件中写;

(3)如果一个写进程正在进行操作,禁止任何读进程度文件。我们需要分两种情况实现该问题:

读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。

写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。

三设计分析

在Windows 7 环境下,创建一个包含n 个线程的控制台进程。用这n 个线程来表示n个读者或写者。每个线程按相应测试数据文件的要求,进行读写操作。请用信号量机制分别实现读者优先和写者优先的读者-写者问题。

读者-写者问题的读写操作限制:

读者-写者的读写限制(包括读者优先和写者优先)1)写-写互斥,即不能有两个写者同时进行写操作

2)读-写互斥,即不能同时有一个读者在读,同时却有一个写者在写 3)读读允许,即可以有2个以上的读者同时读

将所有的读者和所有的写者分别放进两个等待队列中,当读允许时就让读者队列释放一个或多个读者,当写允许时,释放第一个写者操作。读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:1)任意多个读进程可以同时读这个文件;2)一次只有一个写进程可以往文件中写;3)如果一个写进程正在进行操作,禁止任何读进程度文件。我们需要分两种情况实现该问题:

读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。

四 程序实现

程序由两部分组成:

1。读者-写者模块:包括系统调用接口,读者-写者活动描述主程序。系统接口主要功能是通过管道向父进程发送系统调用命令,并读取父进程送来的返回值。

读者-写者活动程序根据临界资源的共享,互斥原则编制,具体见源程序。2。主控模块:主控模块实现系统初始化系统调用命令接收与解释执行,系统调用功能的实现(包括信号量机制),及读者-写者活动过程记录与显示。

初始化系统环境 建立通信管道

启动读者-写者进程 接收系统调用命令 解释执行

系统初始化模块 管道建立模块 进程启动模块 命令解释模块 Wait()Signal()Wakeup()Block()

五 程序调试

测试数据文件格式: 测试数据文件包括n 行测试数据,分别描述创建的n 个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字段,各字段间用空格分隔。第一字段为一个正整数,表示线程序号。第二字段表示相应线程角色,R 表示读者是,W 表示写者。第三字段为一个正数,表示读写操作的开始时间。线程创建后,延时相应时间(单位为秒)后发出对共享资源的读写申请。第四字段为一个正数,表示读写操作的持续时间。当线程读写申请成功后,开始对共享资源的读写操作,该操作持续相应时间后结束,并释放共享资源。

六 结果分析和讨论

在读者写者同时在队列中等待申请资时,读者优先调用资源。而且如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作,即读读允许。

进程1是R操作,在时间3时进入队列,运行时间是5,在它进入时没有进程占用资源,它既占用资源;知道它释放资源,等候的进程有3,4,5;

进程2是W操作,在时间16时进入队列,运行时间是5,在它进入时进程4占用资源,它等待资源,当4释放时占用资源;

进程3是R操作,在时间5时进入队列,运行时间是2,在它进入时进程1占用资源,它等待资源,当进程1释放资源后,由于读者优先,进程3,5同时调 运资源;

进程4是R操作,在时间6时进入队列,运行时间是5,在它进入时进程1占用资源,它等待资源,当进程1释放资源后,由于读者优先,进程3,5占用资源,它依然等待,直到进程3,5都结束;

进程5是W操作,在时间4时进入队列,运行时间是3, 在它进入时进程1占用资源,它等待资源,当进程1释放资源后,由于读者优先,进程3,5同时调运资源;

七 心得体会

这一次课程设计,让我体会很深刻。读者-写者问题经典的线程同步问题的一个模型。经过读者写者问题的编写,我对同步机构应用有了深入的了解。懂得了运用信号量实现进程间的互斥。实现了不让共享资源同时修改。用信号量上的原语操作使临界段问题的解决比较简单明了了。读者写者问题的编写,花的时间很多,也学到很多东西。了解支持多道程序的并发操作系统设计中解决资源共享时进程间的同步与互斥的信号量机制。几天的试验,虽然难度有点大,但只要自己花时间去学习,还是会攻克困难的。

总之,每一次课程设计不仅是我们学习的好机会,而且是我们锻炼实际动手能力的平台,虽然有难度的东西总会让人很抵触,比如在课设过程中有很多郁闷的时候,一个小小的错误一不小心就花去了自己一上午的时间,所以在这个过程中能够磨练人的意志与耐心,最后感谢老师的指导与监督。

八 源代码

#include #include #include #include #include #include #define MAX_PERSON 100 #define READER 0 //读者 #define WRITER 1 //写者 #define END-1 #define R READER #define W WRITER typedef struct _Person { HANDLE m_hThread;//定义处理线程的句柄 int m_nType;//进程类型(读写)int m_nStartTime;//开始时间 int m_nWorkTime;//运行时间 int m_nID;//进程号 }Person;Person g_Persons[MAX_PERSON];int g_NumPerson = 0;long g_CurrentTime= 0;//基本时间片数 int g_PersonLists[] = {//进程队列 1, R, 3, 5, 2, W, 4, 5, 3, R, 5, 2, 4, R, 6, 5, 5, W, 5.1, 3, END,};int g_NumOfReading = 0;int g_NumOfWriteRequest = 0;//申请写进程的个数 HANDLE g_hReadSemaphore;//读者信号 HANDLE g_hWriteSemaphore;//写者信号 bool finished = false;//所有的读完成 //bool wfinished = false;//所有的写完成 void CreatePersonList(int *pPersonList);bool CreateReader(int StartTime,int WorkTime,int ID);bool CreateWriter(int StartTime,int WorkTime,int ID);DWORD WINAPI ReaderProc(LPVOID lpParam);DWORD WINAPI WriterProc(LPVOID lpParam);int main(){ g_hReadSemaphore = CreateSemaphore(NULL,1,100,NULL);//创建信号灯,当前可用的资源数为1,最大为100 g_hWriteSemaphore = CreateSemaphore(NULL,1,100,NULL);//创建信号灯,当前可用的资源数为1,最大为100 CreatePersonList(g_PersonLists);// Create All the reader and writers printf(“Created all the reader and writern...n”);g_CurrentTime = 0;while(true){ g_CurrentTime++;Sleep(300);// 300 ms printf(“CurrentTime = %dn”,g_CurrentTime);if(finished)return 0;} // return 0;} void CreatePersonList(int *pPersonLists){ int i=0;int *pList = pPersonLists;bool Ret;while(pList[0]!= END){ switch(pList[1]){ case R: Ret = CreateReader(pList[2],pList[3],pList[0]);//351,w452,523,654 break;case W: Ret = CreateWriter(pList[2],pList[3],pList[0]);break;} if(!Ret)printf(“Create Person %d is wrongn”,pList[0]);pList += 4;// move to next person list } } DWORD WINAPI ReaderProc(LPVOID lpParam)//读过程 { Person *pPerson =(Person*)lpParam;// wait for the start time while(g_CurrentTime!= pPerson->m_nStartTime){ } printf(“Reader %d is Requesting...n”,pPerson->m_nID);printf(“nn************************************************n”);// wait for the write request WaitForSingleObject(g_hReadSemaphore,INFINITE);if(g_NumOfReading ==0){ WaitForSingleObject(g_hWriteSemaphore,INFINITE);} g_NumOfReading++;ReleaseSemaphore(g_hReadSemaphore,1,NULL);pPerson->m_nStartTime = g_CurrentTime;printf(“Reader %d is Reading the Shared Buffer...n”,pPerson->m_nID);printf(“nn************************************************n”);while(g_CurrentTime <= pPerson->m_nStartTime + pPerson->m_nWorkTime){} printf(“Reader %d is Exit...n”,pPerson->m_nID);printf(“nn************************************************n”);WaitForSingleObject(g_hReadSemaphore,INFINITE);g_NumOfReading--;if(g_NumOfReading == 0){ReleaseSemaphore(g_hWriteSemaphore,1,NULL);//此时没有读者,可以写 } ReleaseSemaphore(g_hReadSemaphore,1,NULL);if(pPerson->m_nID == 4)finished = true;//所有的读写完成 ExitThread(0);return 0;} DWORD WINAPI WriterProc(LPVOID lpParam){ Person *pPerson =(Person*)lpParam;// wait for the start time while(g_CurrentTime!= pPerson->m_nStartTime){} printf(“Writer %d is Requesting...n”,pPerson->m_nID);printf(“nn************************************************n”);WaitForSingleObject(g_hWriteSemaphore,INFINITE);// modify the writer's real start time pPerson->m_nStartTime = g_CurrentTime;printf(“Writer %d is Writting the Shared Buffer...n”,pPerson->m_nID);while(g_CurrentTime <= pPerson->m_nStartTime + pPerson->m_nWorkTime){} printf(“Writer %d is Exit...n”,pPerson->m_nID);printf(“nn************************************************n”);//g_NumOfWriteRequest--;ReleaseSemaphore(g_hWriteSemaphore,1,NULL);if(pPerson->m_nID == 4)finished = true;//所有的读写完成 ExitThread(0);return 0;} bool CreateReader(int StartTime,int WorkTime,int ID){ DWORD dwThreadID;if(g_NumPerson >= MAX_PERSON)return false;Person *pPerson = &g_Persons[g_NumPerson];pPerson->m_nID = ID;pPerson->m_nStartTime = StartTime;pPerson->m_nWorkTime = WorkTime;pPerson->m_nType = READER;g_NumPerson++;// Create an New Thread pPerson->m_hThread= CreateThread(NULL,0,ReaderProc,(LPVOID)pPerson,0,&dwThreadID);if(pPerson->m_hThread == NULL)return false;return true;} bool CreateWriter(int StartTime,int WorkTime,int ID){ DWORD dwThreadID;if(g_NumPerson >= MAX_PERSON)return false;Person *pPerson = &g_Persons[g_NumPerson];pPerson->m_nID = ID;pPerson->m_nStartTime = StartTime;pPerson->m_nWorkTime = WorkTime;pPerson->m_nType = WRITER;g_NumPerson++;// Create an New Thread pPerson->m_hThread= CreateThread(NULL,0,WriterProc,(LPVOID)pPerson,0,&dwThreadID);if(pPerson->m_hThread == NULL)return false;return true;}

第三篇:读者-写者 操作系统实验报告 计算机操作系统

4.1实验二:读者写者问题 4.1.1实验要求

在Windows 环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件(后面有介绍)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。

读者-写者问题的读写操作限制(包括读者优先和写者优先): 1)写-写互斥,即不能有两个写者同时进行写操作。

2)读-写互斥,即不能同时有一个线程在读,而另一个线程在写。3)读-读允许,即可以有一个或多个读者在读。

读者优先的附加限制:如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。

写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态才能开始读操作。

运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始读写操作和结果读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写操作限制。4.1.2测试数据文件格式

测试数据文件包括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.1.3实习分析

可以将所有读者和所有写者分别存于一个读者等待队列和一个写者等待队列中,每当读允许时,就从读者队列中释放一个或多个读者线程进行读操作;每当写允许时,就从写者队列中释放一个写者进行写操作。

1.读者优先

读者优先指的是除非有写者在写文件,否则读者不需要等待。所以可以用一个整型变量read-count记录当前的读者数目,用于确定 是否需要释放正在等待的写者线程(当read-count=0时,表明所有的读者读完,需要释放写者等待队列中的一个写者)。每一个读者开始读文件时,必须修改read-count变量。因此需要一个互斥对象mutex来实现对全局变量read-count修改时的互斥。

另外,为了实现写-写互斥,需要增加一个临界区对象write。当写者发出写请求时,必须申请临界区对象的所有权。通过这种方法,也可以实现读-写互斥,当read-count=1时(即第一个读者到来时),读者线程也必须申请临界区对象的所有权。当读者拥有临界区的所有权时,写者阻塞在临界区对象write上。当写者拥有临界区的所有权时,第一个读者判断完“read-count==1”后阻塞在write上,其余的读者由于等待对read-count的判断,阻塞在mutex上。

2.写者优先

写者优先与读者优先类似。不同之处在于一旦一个写者到来,它应该尽快对文件进行写操作,如果有一个写者在等待,则新到来的读者不允许进行读操作。为此应当添加一个整型变量write-count,用于记录正在等待的写者数目,当write-count=0时,才可以释放等待的读者线程队列。

为了对全局变量write-count实现互斥,必须增加一个互斥对象mutex3。

为了实现写者优先,应当添加一个临界区对象read,当有写者在写文件或等待时,读者必须阻塞在read上。

读者线程除了要对全局变量read-count实现操作上的互斥外,还必须有一个互斥对象对阻塞read这一过程实现互斥。这两个互斥对象分别命名为mutex1和mutex2。4.1.4相关API函数说明

1.CreateThread 函数功能:

该函数创建一个在调用进程的地址空间中执行的线程。函数原型:

HANDLE CreateThread(LPSECURITY-ATTRIBUTES lpThreadAttributes, DWORD dwStackSize,LPTHREAD-START-TOUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LLPDWORD lpThreadId);参数:

·lpThreadAttributes:指向一个SECURITY-ATTRIBUTES结构,该结构决定了返回的句柄是否可被子进程继承。若lpThreadAttributes为NULL,则句柄不能被继承。在Windows NT中该结构的lpSwcurityDescriptor成员定义了新进程的安全性描述符。若lpThreadAttributes为NULL,则线程获得一个默认的安全性描述符。·dwStackSize:定义原始堆栈提交时的大小(按字节计)。系统将该值舍入为最近的页。若该值为0,或小于默认时提交的大小,默认情况是使用与调用线程同样的大小。更多的信息,请看Thread Stack Size。

·lpStartAddress:指向一个LPTHREAD-START-TOUTINE类型的应用定义的函数,该线程执行此函数。该指针还表示远程进程中线程的起始地址。该函数必须存在于远程进程中。

·lpParameter:定义一个传递给该进程的32位值。

·dwCreationFlags:定义控制进程创建的附加标志。若定义CREATE-SUSPENDED标志,线程创建时处于挂起状态,并且直到ResumeThread函数调用时才能运行。若该值为0,则该线程在创建后立即执行。

·lpThreadId:指向一个32位值,它接收该线程的标识符。返回值:

若函数调用成功,返回值为新线程的句柄;若函数调用失败,返回值为NULL。备注:

新线程的句柄创建时设为THREAD-ALL-ACCESS访问权限。若未提供安全性描述符,则该句柄可被任何要求一个线程对象句柄的函数所使用。若提供了安全性描述符,则以后使用该句柄时,将在授权访问以前执行访问检查。若访问检查被拒绝访问,则请求进程不能使用该句柄获得对该线程的访问。线程从lpStartAddress参数定义的函数处开始执行。若该函数返回,系统将默认地认为以调用ExitThread函数的方法终止该线程。使用GetExitCodeThread 函数来获得线程的返回值。

线程创建时拥有THREAD-PRIORITY-NORMAL优先权。使用GetThreadPriority和SetThreadPriority函数可以获得和设置线程的优先权值。

一个线程终止时,该线程对象被设为发信号状态,以满足在该对象上等待的所有进程。一个线程对象始终存在于系统中,直到该线程终止,且它所有的句柄都已通过调用CloseHandle函数关闭。

2.ExitThread 函数功能:

该函数结束一个线程。函数原型:

VOID ExitThread(DWORD dwExitcode); 参数:

·dwExitcode:定义调用线程的退出代码。使用GetExitcodeThread函数来检测一个线程的退出代码。返回值:无。备注:

调用ExitThread函数,是结束一个线程的较好的方法。调用该函数后(或者直接地调用,或者从一个线程过程返回),当前线程的堆栈取消分配,线程终止。若调用该函数时,该线程为进程的最后一个线程,则该线程的进程也被终止。

线程对象的状态变为发信号状态,以释放所有正在等待该线程终止的其他线程。线程的终止状态从STILL-ACTIVATE变为dwExitcode参数的值。

线程结合时不必从操作系统中移去该线程对象。当线程的最后一个句柄关闭时,该线程对象被删除。

3.SLEEP 函数功能:

该函数对于指定的时间间隔挂起当前的执行线程。函数原型:

VOID SLEEP(DWORD dwMilliseconds); 参数:

·dwMilliseconds:定义挂起执行线程的时间,以毫秒(ms)为单位。取值为0时,该线程将如余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一优先级的其他线程,则函数立即返回,该线程继续执行。若取值为INFINITE则造成无限延迟。返回值:

该函数没有返回值。备注:

一个线程可以在调用该函数时将睡眠时间设为0ms,以将剩余的时间片交出。4.CreateMutex 函数功能:

该函数创建有名或者无名的互斥对象。函数原型:

HANDLE CreateMutex(LPSECURITY-ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner,LPCTSTR lpName);参数:

·lpMutexAttributes:指向SECURITY-ATTRIBUTES结构的指针,该结构决定子进程是否能继承返回句柄。如果lpMutexAttributes为NULL,那么句柄不能被继承。

在Windows NT中该结构的lpSwcurityDescriptor成员指定新互斥对象的安全性描述符。若lpThreadAttributes为NULL,那么互斥对象获得默认的安全性描述符。·bInitialOwner:指定互斥对象的初始所属身份。如果该值为TRUE,并且调用者创建互斥对象,那么调用线程获得互斥对象所属身份。否则,调用线程不能获得互斥对象所属身份。判断调用者是否创建互斥对象请参阅返回值部分。

·lpName:指向以NULL结尾的字符串,该字符串指定了互斥对象名。该名字的长度大于MAX-PATH且可以包含除反斜线()路径分隔符以外的任何字符。名字是区分大小写的。

如果lpName与已存在的有名互斥对象相匹配,那么该函数要求用MUTEX-ALL-ACCESS权限访问已存在的对象。在这种情况下,由于参数bInitialOwner已被创建进程所设置,该参数被忽略。如果参数lpMutexAttributes不为NULL,它决定句柄是否解除继承,但是其安全描述符成员被忽略。

如果lpName为NULL,那么创建的互斥对象无名。

如果lpName与已存在的事件、信号量、可等待定时器、作业或者文件映射对象的名字相匹配,那么函数调用失败,并且GetLastError函数返回ERROR-INVALID-HANDLE,其原因是这些对象共享相同的名字空间。

返回值:

如果函数调用成功,返回值为互斥对象句柄;如果函数调用之前,有名互斥对象已存在,那么函数给已存在的对象返回一个句柄,并且函数GetLastError返回ERROR-ALREADY-EXISTS,否则,调用者创建互斥对象。

如果函数调用失败,则返回值为NULL。若想获得更多错误信息,请调用GetLastError函数。

备注:

由函数CreateMutex返回的句柄有MUTEX-ALL-ACCESS权限可以去访问新的互斥对象,并且可用在请求互斥对象句柄的任何函数中。

调用进程中的任何线程可以可以在调用等待函数时指定互斥对象句柄。当指定对象的状态为信号态时,返回单对象等待函数。当任何一个或者所有的互斥对象都为信号态时,返回多对象等待函数指令。等待函数返回后,等待的线程被释放,继续向下执行。

当一个互斥对象不被任何线程拥有时,处于信号态。创建该对象的线程可以使用bInitialOwner标志来请求立即获得对该互斥对象的所有权。否则,线程必须使用等待函数来请求所有权。当互斥对象处于信号态,等待的线程获得对该对象的所有权时,此互斥对象的状态被设置为非信号态,等待函数返回。任意时刻,仅有一个线程能拥有该互斥对象,线程可以使用ReleaseMutex函数来释放对这个互斥对象的所有权。

若线程已经拥有了一个互斥对象,那么它可以重复调用等待函数而不会发生阻塞,一般情况下,用户不会重复等待同一个互斥对象,这种机制防止了线程因等待它已经拥有的互斥对象而发生死锁。然而,线程必须为每一次等待调用一次ReleaseMutex函数来释放该互斥对象。

两个或多个互斥进程可以调用CreateMutex来创建同名的互斥对象,第一个进程实际创建互斥对象,以后的进程打开已存在的互斥对象的句柄。这使得多个进程可以得到同一个互斥对象的句柄,从而减轻了用户的负担,使用户不必判断创建进程是否为第一个启动的进程。使用这种技术时,应该把bInitialOwner标志设为FALSE;否则很难确定开始时哪一个进程拥有该互斥对象。

由于多进程能够拥有相同互斥对象的句柄,通过使用这个对象,可使多进程同步。以下为共享对象机制:

·如果CreateMutex中的lpMutexAttributes参数允许继承,由CreateProcess函数创建的子进程可以继承父进程的互斥对象句柄。

·一个进程可以在调用DuplicateHandle函数时指定互斥对象句柄来创建一个可以被其他进程使用的双重句柄。一个进程在调用OpenMutex或CreateMutex函数时能指定互斥对象名。

·使用CloseHandle函数关闭句柄,进程时系统自动关闭句柄。当最后一个句柄被关闭时,互斥对象被销毁。

5.ReleaseMutex 函数功能:

该函数放弃指定互斥对象的所有权。函数原型:

BOOL ReleaseMutex(HANDLE hMutex); 参数:

·hMutex:互斥对象句柄。为CreateMutex或OpenMutex函数的返回值。返回值:

如果函数调用成功,那么返回值是非零值;如果函数调用失败,那么返回值是零值。若想获得更多错误信息,请调用GetLastError函数。

备注:

如果调用线程不拥有互斥对象,ReleaseMutex函数失败。一个线程通过调用等待函数拥有互斥对象。创建该互斥对象的线程也拥有互斥对象,而不需要调用等待函数。当互斥对象的所有者线程不再需要互斥对象时,它可以调用ReleaseMutex函数。

当一个线程拥有一个互斥对象后,它可以用该互斥对象多次调用等待函数而不会阻塞。这防止一个线程等待一个它拥有的互斥对象时出现死锁。不过,为了释放所有权,该线程必须为每一个等待操作调用一次ReleaseMutex函数。

6.WaitForSingleObject 函数功能:

当下列情况之一发生时该函数返回:(1)指定对象处于信号态;(2)超时。函数原型:

DWORD waitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);参数:

·hHandle:等待对象句柄。若想了解指定句柄的对象类型列表,参阅下面备注部分。

在Windows NT中,句柄必须有SYNCHRONIZE访问权限。若想获得更多的信息,请查看Standard Access Rights。

·dwMilliseconds:指定以毫秒为单位的超时间隔。如果超时,即使对象的状态是非信号态的并且没有完成,函数也返回。如果dwMilliseconds是0,函数测试对象的状态并立即返回;如果dwMilliseconds是INFINITE,函数从不超时。返回值:

如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:

·WAIT-ABANDONED:指定对象是互斥对象,在线程被终止前,线程没有释放互斥对象。互斥对象的所属关系被授予调用线程,并且该互斥对象被置为非信号态。·WAIT-OBJECT-0:指定对象的状态被置为信号态。

·WAIT-TIMEOUT:超时,并且对象的状态为非信号态。

如果函数调用失败,返回值是WAIT-FAILED。若想获得更多错误信息,请调用GetLastError函数。

备注:

waitForSingleObjects函数决定等待条件是否被满足。如果等待条件并没有被满足,调用线程进入一个高效的等待状态,当等待满足条件时占用非常少的处理机时间。

在运行前,一个等待函数修改同步对象类型的状态。修改仅发生在引起函数返回的对象身上。例如,信号的计数减1。

WaitForSingleObjects函数能等待的对象包括:Change notification(改变通告);Consoleinput(控制台输入);Event(事件);Job(作业);Mutex(互斥对象);Process(进程);Semaphore(信号量);Thread(线程);Waitable timer(可等待定时器)。

当使用等待函数或代码直接或间接创建窗口时,一定要小心。如果一个线程创建了任何窗口,它必须处理进程消息。消息广播被发送到系统的所有窗口。一个线程用没有超时的等待函数也许会引起系统死锁。间接创建窗口的两个例子是DDE和COM CoInitialize。因此,如果用户有一个创建窗口的线程,用MsgWaitForMultipleObjects或MsgWaitForMultipleObjectsEx函数,而不要用SignalObjectAndWait函数。

7.WaitForMultipleObjects 函数功能:

WaitForMultipleObjects函数当下列条件之一满足时返回:(1)任意一个或全部指定对象处于信号态;(2)超时间隔以过。

函数原型:

DWORD WaitForMultipleObjects(DWORD nCount,CONST HANDLE *lpHandles, BOOL fWaitAll,DWORD dwMilliseconds);参数:

·nCount:指定由lpHandles所指向的数组中的句柄对象数目MAXIMUM-WAIT-OBJECTS。

·lpHandles:指向对象句柄数组的指针。该数组可以包含不同类型对象的句柄。在Windows NT中,句柄必须有SYNCHRONIZE访问权限。若想获得更多的信息,请查看Standard Access Rights。

·fWaitall:指定等待类型。如果为TRUE,当lpHandles指向的数组里的全部对象为信号态时,函数返回。如果为FALSE,当由lpHandles指向的数组里的任一对象为信号态时,函数返回。对于后者,返回值指出引起函数返回的对象。

·dwMilliseconds:指定以毫秒为单位的超时间隔。如果超时,即使bWaitAll参数指定的条件没有满足,函数也返回。如果dwMilliseconds是0,函数测试对象的状态并立即返回;如果dwMilliseconds是INFINITE,函数从不超时。

返回值:

如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:

·WAIT-OBJECT-0到WAIT-OBJECT-0+nCount-1:如果bWaitAll为TRUE,那么返回值表明所有指定对象的状态为信号态。如果bWaitAll为FALSE,那么返回值减去WAIT-OBJECT-0表明引起函数返回的对象的pHandles数组索引。如果多于一个对象变为信号态,则返回的是数组索引最小的信号态对象索引。

·WAIT-ABANDONED-0到·WAIT-ABANDONED-0+ nCount-1:如果bWaitAll为TRUE,那么返回值表明所有指定对象的状态为信号态,并且至少一个对象是已放弃的互斥对象。如果bWaitAll为FALSE,那么返回值减去WAIT-OBJECT-0表明引起函数返回的放弃互斥对象的pHandles数组索引。

·WAIT-TIMEOUT:超时并且由参数bWaitAll指定的条件没有满足。

如果函数调用失败,返回值是WAIT-FAILED。若想获得更多错误信息,请调用GetLastError函数。

8.CreateSemapore 函数功能:

该函数是创建一个有名或者无名信号对象。函数原型:

HANDLE CreateSwmaphore(LPSECURITY-ATTRIBUTES lpAttributes,LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);参数:

·lpAttributes:安全属性。如果是NULL就表示要使用默认属性。·lInitialCount:Semapore的初值。必须大于或等于0,并且小于或等于MaximumCount。·lMaximumCount:Semapore的最大值。这也就是在同一时间内能够锁住Semapore之线程的最多个数。

·lpName:Semapore的名称(一个字符串)。任何线程(或进程)都可以根据这一名称引用到这个Semaphore。这个值可以是NULL,意思是产生一个没有名字的Semaphore。

返回值:

如果成功就传回一个handle,否则传回NULL。不论哪一种情况,GetLastError都会传回一个合理的结果。如果指定的Semaphore名称已经存在,则该函数还是成功的,GetLastError会传回ERROR_ALREADY_EXISTS。

9.ReleaseSemaphore 函数功能:

该函数将指定信号对象的计数增加一个指定的数量。函数原型:

BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount,LPLONG lpPreviousCount);参数:

·hSemaphore:Semaphore的handle。

·lReleaseCount:Semaphore现值的增额。该值不可以是负值或0。·lpPreviousCount:借此返回Semaphore原来的值。返回值:

如果成功,则返回TRUE。否则返回FALSE。失败时可调用GetLastError获得原因。备注:

无论ReleaseSemaphore对于Semaphore所造成的当前值怎样增加,都绝对不会超过CreateSemaphore时所指定的ImaximumCount。

请记住,lpPreviousCount所传回来的是一个瞬间值。你不可以把lReleaseCount加上* lpPreviousCount,就当做是Semaphore的当前值,因为其他线程可能已经改变了Semaphore的值。

与mutex不同的是,调用ReleaseSemaphore的那个线程,并不一定就是调用WaitXxx 的那个线程。任何线程都可以在任何时候调用ReleaseSemaphore,解除被任何线程锁定的Semaphore。10.InitializeCriticalSection 函数功能:

该函数初始化临界区对象。函数原型:

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数:

·lpCriticalSection:指向临界区对象的指针。备注:

单进程的所有线程可以使用互斥同步机制的临界区对象。但是,不能保证线程获得临界区所有权的顺序,系统将对所有线程公平处理。

进程负责分配临界区对象使用的存储空间,这可以通过声明CRITICAL_SECTION类型的变量来完成。在使用临界区之前,该进程的一些线程必须使用InitializeCriticalSection或InitializeCriticalSectionAndSectiom函数来初始化该临界区对象。

11.EnterCriticalSection 函数功能:该函数是等待指定临界区对象的所有权。当调用线程被赋予所有权时,该函数返回。

函数原型:

VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数:

·lpCriticalSection:指向临界区对象的指针。12.LeaveCriticalSection 函数功能:该函数释放指定临界区对象的所有权。函数原型:

VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数:

·lpCriticalSection:指向临界区对象的指针。4.1.5参考源代码

下面的程序已经在Windows 2000/XP上实现。用VC6.0创建源文件,将输入文件命名为thread.dat并放在与源文件相同的文件夹内,编译运行即可(本节中的参考源代码仅供参考)。

#include “windows.h” #include #include #include #include #include #include

#define READER 'R'

// 读者 #define WRITER 'W'

// 写者

#define INTE_PER_SEC 1000

// 每秒时钟中断数目。#define MAX_THREAD_NUM 64

// 最大线程数目 #define MAX_FILE_NUM 32

// 最大数据文件数目 #define MAX_STR_LEN 32

// 字符串长度

int readcount=0;

// 读者数目 int writecount=0;

// 写者数目

CRITICAL_SECTION RP_Write;

//临界区 CRITICAL_SECTION cs_Write;CRITICAL_SECTION cs_Read;struct ThreadInfo { int

serial;

// 线程序号

char entity;

//线程类别(判断读者线程还是写者线程)double delay;double persist;};

/////////////////////////////////////////////////////////////////////////// // 读者优先--读者线程 //p:读者线程信息

void RP_ReaderThread(void* p){

//互斥变量

HANDLE h_Mutex;h_Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex_for_readcount”);

DWORD wait_for_mutex;

//等待互斥变量所有权 DWORD m_delay;

// 延迟时间

DWORD m_persist;

// 读文件持续时间 int m_serial;

//线程序号 //从参数中获得信息

m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);

//延迟等待

printf(“Reader thread %d sents the reading require.n”,m_serial);

// 等待互斥信号,保证对readcount的访问、修改互斥 wait_for_mutex=WaitForSingleObject(h_Mutex,-1);//读者数目增加 readcount++;if(readcount==1){

//第一个读者,等待资源

EnterCriticalSection(&RP_Write);} ReleaseMutex(h_Mutex);

//释放互斥信号

//读文件

printf(“Reader thread %d begins to read file.n”,m_serial);Sleep(m_persist);

// 退出线程

printf(“Reader thread %d finished reading file.n”,m_serial);//等待互斥信号,保证对readcount的访问、修改互斥 wait_for_mutex=WaitForSingleObject(h_Mutex,-1);//读者数目减少 readcount--;if(readcount==0){

//如果所有读者读完,唤醒写者

LeaveCriticalSection(&RP_Write);} ReleaseMutex(h_Mutex);

//释放互斥信号 }

/////////////////////////////////////////////////////////////////////////// // 读者优先--写者线程 //p:写者线程信息

void RP_WriterThread(void* p){ DWORD m_delay;

// 延迟时间

DWORD m_persist;

// 写文件持续时间 int m_serial;

//线程序号 //从参数中获得信息

m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);

//延迟等待

printf(“Writer thread %d sents the writing require.n”,m_serial);// 等待资源

EnterCriticalSection(&RP_Write);

//写文件 printf(“Writer thread %d begins to Write to the file.n”,m_serial);Sleep(m_persist);

// 退出线程

printf(“Writer thread %d finished Writing to the file.n”,m_serial);//释放资源

LeaveCriticalSection(&RP_Write);}

/////////////////////////////////////////////////////////////////////////// // 读者优先处理函数 //file:文件名

void ReaderPriority(char* file){ DWORD n_thread=0;

//线程数目 DWORD thread_ID;

//线程ID DWORD wait_for_all;

//等待所有线程结束 //互斥对象

HANDLE h_Mutex;h_Mutex=CreateMutex(NULL,FALSE,“mutex_for_readcount”);

//线程对象的数组

HANDLE h_Thread[MAX_THREAD_NUM];ThreadInfo thread_info[MAX_THREAD_NUM];

readcount=0;

// 初始化 readcount

InitializeCriticalSection(&RP_Write);

//初始化临界区 ifstream inFile;inFile.open(file);

//打开文件 printf(“Reader Priority:nn”);while(inFile){

//读入每一个读者、写者的信息

inFile>>thread_info[n_thread].serial;

inFile>>thread_info[n_thread].entity;

inFile>>thread_info[n_thread].delay;

inFile>>thread_info[n_thread++].persist;

inFile.get();} for(int i=0;i<(int)(n_thread);i++){

if(thread_info[i].entity==READER || thread_info[i].entity=='R')

{

h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_ReaderThread),&thread_info[i],0,&thread_ID);//创建读者线程

} else{

//创建写者线程

h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);

} }

//等待所有线程结束

wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);printf(“All reader and writer have finished operating.n”);}

/////////////////////////////////////////////////////////////////////////// // 写者优先--读者线程 //p:读者线程信息

void WP_ReaderThread(void* p){

//互斥变量

HANDLE h_Mutex1;h_Mutex1=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex1”);HANDLE h_Mutex2;h_Mutex2=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex2”);

DWORD wait_for_mutex1;

//等待互斥变量所有权 DWORD wait_for_mutex2;DWORD m_delay;

// 延迟时间

DWORD m_persist;

// 读文件持续时间 int m_serial;

//线程序号 //从参数中获得信息

m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);

//延迟等待

printf(“Reader thread %d sents the reading require.n”,m_serial);wait_for_mutex1= WaitForSingleObject(h_Mutex1,-1);//进入读者临界区

EnterCriticalSection(&cs_Read);// 阻塞互斥对象mutex2,保证对readcount的访问、修改互斥 wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);//修改读者数目 readcount++;if(readcount==1){

//如果是第一个读者,等待写者写完

EnterCriticalSection(&cs_Write);} ReleaseMutex(h_Mutex2);

//释放互斥信号mutex2 // 让其他读者进入临界区

LeaveCriticalSection(&cs_Write);ReleaseMutex(h_Mutex1);//读文件

printf(“Reader thread %d begins to read file.n”,m_serial);Sleep(m_persist);// 退出线程

printf(“Reader thread %d finished reading file.n”,m_serial);// 阻塞互斥对象mutex2,保证对readcount的访问、修改互斥 wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);readcount--;if(readcount==0){

// 最后一个读者,唤醒写者

LeaveCriticalSection(&cs_Write);} ReleaseMutex(h_Mutex2);

//释放互斥信号 } /////////////////////////////////////////////////////////////////////////// // 写者优先--写者线程 //p:写者线程信息

void WP_WriterThread(void* p){ DWORD m_delay;

// 延迟时间

DWORD m_persist;

// 写文件持续时间 int m_serial;

//线程序号 DWORD wait_for_mutex3;//互斥对象

HANDLE h_Mutex3;h_Mutex3= OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex3”);

//从参数中获得信息

m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);

//延迟等待 printf(“Writer thread %d sents the writing require.n”,m_serial);

// 阻塞互斥对象mutex3,保证对writecount的访问、修改互斥 wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);writecount++;

//修改读者数目 if(writecount==1){

//第一个写者,等待读者读完

EnterCriticalSection(&cs_Read);} ReleaseMutex(h_Mutex3);

//进入写者临界区

EnterCriticalSection(&cs_Write);//写文件

printf(“Writer thread %d begins to Write to the file.n”,m_serial);Sleep(m_persist);

// 退出线程

printf(“Writer thread %d finishing Writing to the file.n”,m_serial);

//离开临界区

LeaveCriticalSection(&cs_Write);

// 阻塞互斥对象mutex3,保证对writecount的访问、修改互斥 wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);writecount--;

//修改读者数目 if(writecount==0){

//写者写完,读者可以读

LeaveCriticalSection(&cs_Read);} ReleaseMutex(h_Mutex3);}

/////////////////////////////////////////////////////////////////////////// // 写者优先处理函数 //file:文件名

void WriterPriority(char* file){ DWORD n_thread=0;

//线程数目 DWORD thread_ID;

//线程ID DWORD wait_for_all;

//等待所有线程结束

//互斥对象

HANDLE h_Mutex1;h_Mutex1=CreateMutex(NULL,FALSE,“mutex1”);HANDLE h_Mutex2;h_Mutex2=CreateMutex(NULL,FALSE,“mutex2”);HANDLE h_Mutex3;h_Mutex3=CreateMutex(NULL,FALSE,“mutex3”);

//线程对象

HANDLE h_Thread[MAX_THREAD_NUM];ThreadInfo thread_info[MAX_THREAD_NUM];

readcount=0;

// 初始化 readcount writecount=0;

// 初始化writecount InitializeCriticalSection(&cs_Write);

//初始化临界区 InitializeCriticalSection(&cs_Read);ifstream inFile;inFile.open(file);

//打开文件 printf(“Writer Priority:nn”);while(inFile){

//读入每一个读者、写者的信息

inFile>>thread_info[n_thread].serial;

inFile>>thread_info[n_thread].entity;

inFile>>thread_info[n_thread].delay;

inFile>>thread_info[n_thread++].persist;

inFile.get();} for(int i=0;i<(int)(n_thread);i++){

if(thread_info[i].entity==READER || thread_info[i].entity=='R')

{

//创建读者线程

h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);

} else{

//创建写者线程

h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WP_WriterThread),&thread_info[i],0,&thread_ID);

} }

//等待所有线程结束

wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);printf(“All reader and writer have finished operating.n”);} /////////////////////////////////////////////////////////////////////////////// //主函数

int main(int argc,char* argv[]){ char ch;

while(true){

//打印提示信息

printf(“************************************************n”);

printf(“

1:Reader Priorityn”);

printf(“

2:Writer Priorityn”);

printf(“

3:Exit Priorityn”);

printf(“************************************************n”);

printf(“Enter your choice(1,2 or 3): ”);

//如果输入信息不正确,继续输入

do{

ch=(char)_getch();

}while(ch!= '1' &&ch!= '2' && ch!= '3');

system(“cls”);

//选择3,返回

if(ch=='3')

return 0;

//选择1,读者优先

else if(ch=='1')

ReaderPriority(“thread.dat”);

//选择2,写者优先

else if(ch=='2')

WriterPriority(“thread.dat”);

//结束

printf(“nPress Any Key To Continue: ”);

_getch();

system(“cls”);} return 0;}

说明:

在Win32 API中,互斥对象Mutex与P、V中的互斥信号量有类似的地方,但也有不同:在P、V操作中的互斥信号量可以有一个任意大小的初值,但互斥对象Mutex没有,它可以被看成是初值为1的互斥信号量。而且一个线程在取得Mutex的所有权之后,即使不调用ReleaseMutex函数,在线程结束时,线程也会自动释放Mutex的所有权。

临界区对象CriticalSection则与P、V操作中初值为1的互斥信号量语意相同。它在线程结束时会将CriticalSection的所有权传递给它的同类型线程。这样就可以满足在一个线程中申请所有权,在另一个线程释放所有权的要求。在读者优先中的write互斥信号量以及写者优先中的read和write互斥信号量就应该用CriticalSection实现而不应该用Mutex。

用WaitForSingleSignal函数可以获得一个Mutex的所有权,类似于P操作,而ReleaseMutex函数可以释放一个Mutex的所有权,类似于V操作。

用EnterCriticalSection函数可以进入一个CriticalSection,类似于P操作,而LeaveCriticalSection函数离开一个CriticalSection,类似于V操作。

备注:

ReaderPriority和WritePriority函数最后都有

wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);是因为主函数要等待所有的线程都结束之后才退出。因为不知道有多少线程,所以源文件最初有:

#define MAX_THREAD_NUM 64 //最大线程数目

即线程最多不能超过MAX_THREAD_NUM个。线程对象的数组大小为MAX_THREAD_NUM。如果创建的线程没有那么多,空间会有浪费,但是可以达到牺牲空间来节省时间的目的。

有的书上还有其他的处理方法。一种是在主函数的最后加上Sleep(1000),即通过主函数睡眠的方法等待其他线程结束,这当然不是一种很好的方法,因为睡眠等待的时间没法控制。另一种方法是增加循环变量threadCount(线程的个数),每个线程结束的就会执行语句

threadCount--;主函数的最后测试:

while(threadcount>0);但是这种方式会让主函数循环等待,浪费了CPU资源。

相比之下,考虑到运行效率,还是实例中给出的方法比较好写些。

第四篇:操作系统课程设计报告——读者写者问题

操作系统课程设计

题:读者写者问题 姓

名:赫前进 班

级:1020552 学

号102055211 指导教师:叶瑶 提交时间:2012/12/30

(一)实验目的

1.进一步理解 “临界资源” 的概念;

2.把握在多个进程并发执行过程中对临界资源访问时的必要约束条件; 3.理解操作系统原理中 “互斥” 和 “同步” 的涵义。

(二)实验内容

利用程序设计语言编程,模拟并发执行进程的同步与互斥(要求:进程数目不少于 3 个)。

(三)、程序分析

读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:(1)任意多个读进程可以同时读这个文件;(2)一次只有一个写进程可以往文件中写;

(3)如果一个写进程正在进行操作,禁止任何读进程度文件。

实验要求用信号量来实现读者写者问题的调度算法。实验提供了signal类,该类通过P()、V()两个方法实现了P、V原语的功能。实验的任务是修改Creat_Writer()添加写者进程,Creat_Reader()创建读者进程。Reader_goon()读者进程运行函数。读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。

读者优先的附加限制:如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作。

写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。

写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。

在Windows 7 环境下,创建一个控制台进程,此进程包含 n 个线程。用这 n 个线程来表示 n 个读者或写者。每个线程按相应测试数据文件(格式见下)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者/写者问题。运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写操作限制。

测试数据文件包括 n 行测试数据,分别描述创建的 n 个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括4个字段,各个字段间用空格分隔。

Ø

第一个字段为一个正整数,表示线程序号

Ø

第二个字段表示相应线程角色,R 表示读者,W 表示写者

Ø

第三个字段为一个正数,表示读/写操作的开始时间:线程创建后,延迟相应时间(单位为秒)后发出对共享资源的读/写请求

Ø

第四个字段为一正数,表示读/写操作的持续时间:线程读写请求成功后,开始对共享资源的读/写操作,该操作持续相应时间后结束,并释放共享资源 例如: 1 R 3 5 2 W 4 5 3 R 5 2 4 R 6 5 5 W 5.1 3 读者写者问题是操作系统中经典的互斥问题:一块数据被多个读者和写者的访问,需要考虑读写互斥、写写互斥(可以同时由多个读者读取)。具体的又可以分为读者优先和写者优先两类。读者优先算法:

当新的读者到来的时候,若当前正有读者在进行读操作,则该读者无需等待前面的写操作完成,直接进行读操作。设置两个互斥信号量:

rwmutex 用于写者与其他读者/写者互斥的访问共享数据 rmutex 用于读者互斥的访问读者计数器readcount var rwmutex, rmutex : semaphore := 1,1 ; int readcount = 0;cobegin

readeri begin // i=1,2,„.P(rmutex);

Readcount++;

If(readcount == 1)P(rwmutex);

V(rmutex);

读数据;

P(rmutex);

Readcount--;

If(readcount == 0)V(rwmutex);

V(rmutex);

End

Writerj begin // j = 1,2,„.P(rwmutex);

写更新;

V(rwmutex);

End Coend 写者优先: 条件:

1)多个读者可以同时进行读

2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)

3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)设置三个互斥信号量:

rwmutex 用于写者与其他读者/写者互斥的访问共享数据 rmutex 用于读者互斥的访问读者计数器readcount nrmutex 用于写者等待已进入读者退出,所有读者退出前互斥写操作 var rwmutex, rmutex,nrmutex : semaphore := 1,1,1 ; int readcount = 0;cobegin

readeri begin // i=1,2,„.P(rwmutex);

P(rmutex);

Readcount++;

If(readcount == 1)P(nrmutex);//有读者进入,互斥写操作

V(rmutex);

V(rwmutex);// 及时释放读写互斥信号量,允许其它读、写进程申请资源

读数据;

P(rmutex);

Readcount--;

If(readcount == 0)V(nrmutex);//所有读者退出,允许写更新

V(rmutex);

End

Writerj begin // j = 1,2,„.P(rwmutex);// 互斥后续其它读者、写者

P(nrmutex);//如有读者正在读,等待所有读者读完

写更新;

V(nrmutex);//允许后续新的第一个读者进入后互斥写操作

V(rwmutex);//允许后续新读者及其它写者

End Coend //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*---------函数声明---------*/ void Creat_Writer();

//添加一个写者 void Del_Writer();

//删除一个写者 void Creat_Reader();

//添加一个读者

void Reader_goon();

//读者进程运行函数 void R_Wakeup();

//唤醒等待读者 void Del_Reader();

//删除一个读者 void Show();

//显示运行状态

/*===============

class

signal

===============*/ class signal //信号量对象.{ private: int value;int queue;

//用int型数据模拟等待队列.public: signal();signal(int n);int P();//检查临界资源 int V();//释放临界资源 int Get_Value();int Get_Queue();};//////////////////////////////////////////////////////////////////// #include

#include #include #include using namespace std;const int MaxThread=20;

struct ThreadInfo

{

int num;

char type;

double start;

double time;

}thread_info[MaxThread];

HANDLE hX;

HANDLE hWsem;

HANDLE thread[MaxThread];int readcount;double totaltime;

void WRITEUNIT(int iProcess){

printf(“Thread %d begins to write.n”,iProcess);

Sleep((DWORD)(thread_info[iProcess-1].time*1000));

printf(“End of thread %d for writing.n”,iProcess);}

void READUNIT(int iProcess){

printf(“Thread %d begins to read.n”,iProcess);

Sleep((DWORD)(thread_info[iProcess-1].time*1000));

printf(“End of thread %d for reading.n”,iProcess);}

DWORD

WINAPI

reader(LPVOID

lpVoid){

int iProcess

=

*(int*)lpVoid;

Sleep((DWORD)(thread_info[iProcess-1].start*1000));

DWORD wait_for=WaitForSingleObject(hX,INFINITE);

printf(“Thread %d requres reading.n”,iProcess);

readcount++;

if(readcount==1)WaitForSingleObject(hWsem,INFINITE);

ReleaseMutex(hX);

READUNIT(iProcess);

wait_for=WaitForSingleObject(hX,INFINITE);

readcount--;

if(readcount==0)

ReleaseSemaphore(hWsem,1,0);

ReleaseMutex(hX);

return iProcess;}

DWORD

WINAPI

writer(LPVOID

lpVoid){

int iProcess

=

*(int*)lpVoid;

Sleep((DWORD)(thread_info[iProcess-1].start*1000));

printf(“Thread %d requres writing.n”,iProcess);

DWORD wait_for=WaitForSingleObject(hWsem,INFINITE);

WRITEUNIT(iProcess);

ReleaseSemaphore(hWsem,1,0);

return iProcess;}

int main(){

int threadNum;

int threadcount;

ifstream file;

hX=CreateMutex(NULL, FALSE, NULL);

hWsem=CreateSemaphore(NULL,1,1,NULL);

//???

readcount=0;

threadcount=0;

totaltime=0;

file.open(“thread.dat”,ios::in);

if(file==0)

{

printf(“File Open Error.n”);

return 0;

}

while(file>>threadNum)

{

thread_info[threadNum-1].num=threadNum;

file>>thread_info[threadNum-1].type;

file>>thread_info[threadNum-1].start;

file>>thread_info[threadNum-1].time;

totaltime+=thread_info[threadNum-1].time;

switch(thread_info[threadNum-1].type)

{

case 'W':

printf(“Creating Thread %d writing.n”,thread_info[threadNum-1].num);

thread[threadNum-1]

=

CreateThread(NULL, &thread_info[threadNum-1].num,0,0);

break;

case 'R':

printf(“Creating Thread %d reading.n”,thread_info[threadNum-1].num);

thread[threadNum-1]

=

CreateThread(NULL, &thread_info[threadNum-1].num,0,0);

break;

}

threadcount++;

}

file.close();

Sleep((DWORD)(totaltime*1000));

return 1;} ////////////////////////////////////////////////////////////////////////////////// semaphore fmutex = 1 , rdcntmutex = 1;// fmutex--> access to file;rdcntmutex--> access to readcount int readcount = 0;void reader(){

while(1)

for 0,writer, for 0,reader,{

P(rdcntmutex);

if(readcount==0)

P(fmutex);

readcount = readcount + 1;

V(rdcntmutex);

// Do read operation

P(rdcntmutex);

readcount = readcount1;

if(readcount==0)

V(fmutex);

V(rdcntmutex);

} } void writer(){

while(1)

{

P(wtcntmutex);

if(writecount==0)

P(queue);

writecount = writecount + 1;

V(wtcntmutex);

P(fmutex);

// Do write operation

V(fmutex);

P(wtcntmutex);

writecount = writecount-1;

if(writecount==0)

V(queue);

V(wtcntmutex);

} }

第五篇:怎么写生物实验报告

实验报告一般分为以下几个部分:

一、实验名称。

二、实验原理。将该实验的主要原理用简明扼要的语言进行归纳总结。

三、实验仪器和材料。如果所用仪器和材料较多,可写重要的部分,常用的可以不写。

四、实验步骤。该实验如何操作的,方法和顺序。可以用方框图表示,这样一目了然。

五、实验结果。将该实验最后结果用文字或图表的方式进行表达。推荐用表格或图进行表示。要注意将度量单位写清楚。

六、实验讨论。该部分主要对上述实验结果进行讨论。有的是对实际操作中实验现象或结果和实验指导不一致的原因进行讨论,有的是对实际操作中产生的实验现象的原理或原因进行讨论,有的是对实际操作中可以改进的方法进行讨论,有的是对该实验的进一步应用进行讨论等等。

下载读者写者实验报告word格式文档
下载读者写者实验报告.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐

    书法者必写繁体字

    书法人必写繁体字,这是书法文字常识,所以应学习总结,避免差错。如茶"几"写成几何之"幾","皇后"写成前后之"後",搅"和"写成和平之"龢",意我之"余"写成多余之"餘",千万"里"写成里外之......

    “者”字型排比句40例,写材料通俗易懂!

     1.做改革开放路上坚毅的“忠实信仰者”,做改革开放路上有担当的“仰望星空者”,做改革开放路上只争朝夕的“脚踏实地者”。2.坚定政治忠诚,做政治上的清醒明白者;坚守政治担当......

    待岗者巧写“求职书”

    待岗者巧写“求职书”,关键词是求职,当你走进熙熙攘攘的招聘会,好容易找到家中意的单位,一个适合你的“空缺”,摊前却堆满了人,你费力挤进去,可工作人员忙得焦头烂额,根本无暇听你......

    读者读书笔记

    《读者》读书笔记 ① 《母亲不是圣人》,讲得是母亲的一个女儿总因为母亲的信心而耿耿于怀,直至有一天,她忽然明白:真正能完美唯有那些圣人。而母亲,不是圣人,虽然她爱每一个孩子,......

    读者观后感

    我一直都很喜欢《读者》这本书,觉得它里面每篇幅短短的故事,或让你深受心灵的洗涤,或告诉你一个深深的道理,让人印象深刻。最近和儿子亲子阅读,我建议大家一起看《读者》吧,儿子......

    读者读后感(大全)

    读者读后感范文(精选5篇)当认真看完一本名著后,相信大家一定领会了不少东西,是时候静下心来好好写写读后感了。是不是无从下笔、没有头绪?下面是小编精心整理的读者读后感范文(精......

    读者读书笔记(通用)(合集)

    读者读书笔记范文(通用10篇)当品读完一部作品后,大家一定对生活有了新的感悟和看法,写一份读书笔记,记录收获与付出吧。想必许多人都在为如何写好读书笔记而烦恼吧,下面是小编为大......

    《读者读后感》

    最近我读了一本叫做《读者》的杂志,感受匪浅,《读者读后感》。下面我就把我的体会和心得给大家说说。《读者》这本书告诉了我许多的道理,让我知道了更多的知识、开阔了视野、知......