一、实验题目
读者写者问题
二、实验目的
1、通过编写和调试程序以加深对进程、线程管理方案的理解;
2、熟悉 Windows 多线程程序设计方法;
三、实验条件和环境
硬件设备:多台计算机。
系统软件:windows 操作系统,Visual C++6.0 编译环境。
四、实验原理
读者写者问题是一个用信号量实现的经典进程同步问题。在系统中,一个
数据集( 如文件或记录) 被几个并发进程共享,这些线程分两类,一部分只要求
进行复操作,称之为“读者”;另一类要求写或修改操作,我们称之为“写
者”。一般而言,对一个数据集,为了保证数据的完整性、正确性,允许多个
读者进程同时访问,但是不允许一个写者进程同其它任何一个进程(读者或者
写者)同时访问,而这类问题就称之为“读者写者”问题。
读者优先的算法在操作系统相关的书籍中都有介绍,这是一种最简单的解
决办法。当没有写进程正在访问共享数据集时,读进程可以进入访问,否则必
须等待。而读者优先的算法存在“饿死写者”线程的问题。只要有读者不断到
来,写者就要持久地等待,直到所有的读者都读完且没有新的读者到来时写者
才能写数据集。而在很多情况下我们需要避免“饿死写者”,故而采用写者优
先算法。
五、实验方法
在 Windows 环境下,创建一个控制台进程,此进程包含 n 个线程。用这 n
个线程来表示 n 个读者或写者。每个线程按相应测试数据文件(后面介绍)的
要求进行读写操作。用信号量机制分别实现读者优先和写者优先问题。
读者-写者问题的读写操作限制(包括读者优先和写者优先)
1)写-写互斥:不能有两个写者同时进行写操作
2)读-写互斥:不能同时有一个线程在读,而另一个线程在写。
3)读-读允许:可以有一个或多个读者在读。
读者优先的附加限制:如果读者申请进行读操作时已有另一个读者正在进
行读操作,则该读者可直接开始读操作。
写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等
待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操
作。
运行结果显示要求:要求在每个线程创建、发出读写申请、开始读写操作
和结束读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写
操作限制。
1
测试数据文件包括 n 行测试数据,分别描述创建的 n 个线程是读者还是写
者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字段,每个
字段间用空格分隔。第 1 个字段为正整数,表示线程的序号。第 2 个字段表示
线程的角色,R 表示读者,W 表示写者。第 3 个字段为一个正数,表示读写开
始时间:线程创建后,延迟相应时间(单位为秒)后发出对共享资源的读写申
请。第 4 个字段为一个正数,表示读写操作的延迟时间。当线程读写申请成功
后,开始对共享资源进行读写操作,该操作持续相应时间后结束,释放该资
源。
下面是一个测试数据文件的例子(在记事本手工录入数据):
1 R 3 5
2 W 4 5
3 R 5 2
4 R 6 5
5 W 5.1 3
六、实验结果及结论
1、读者优先
在读者写者同时在队列中等待申请资源时,读者优先调用资源。而且如果
一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开
始读操作,即读读允许。
进程 1 是 R 操作,在时间 3 时进入队列,运行时间是 5,在它进入时没有
进程占用资源,它就占用资源;它释放资源时,等候的进程有 2 和 5;
进程 2 是 W 操作,在时间 4 时进入队列,运行时间是 5,在它进入时进程
1 占用资源,它等待资源,当 1 释放资源后,进程 4 在占用资源,它依然等待,
直到进程 4 结束;
进程 3 是 R 操作,在时间 5 时进入队列,运行时间是 2,在它进入时进程 1
占用资源,由于读读允许,它和进程 1 同时调用资源;
进程 4 是 R 操作,在时间 6 时进入队列,运行时间是 5,在它进入时进程 1
和 3 占用资源,由于读读允许,它和进程 1、3 同时调用资源;
进程 5 是 W 操作,在时间 5.1 时进入队列,运行时间是 3,在它进入时进
程 1 占用资源,它等待资源,当 1 释放资源后,进程 4 在占用资源,它依然等
待,当 4 释放资源后,进程 2 占用资源,由于写写互斥,它依然等待,直到进
程 2 结束。
2、写者优先
如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该
读者必须等到没有写者处于等待状态后才能开始读操作。
进程 1 是 R 操作,在时间 3 时进入队列,运行时间是 5,在它进入时没有
进程占用资源,它就占用资源;它释放资源时,等候的进程有 2、3、4、5;
进程 2 是 W 操作,在时间 4 时进入队列,运行时间是 5,在它进入时进程
1 占用资源,它等待资源,当 1 释放资源后,由于写者优先,它就占用资源,
它释放资源时,等候的进程有 3、4、5;
进程 3 是 R 操作,在时间 5 时进入队列,运行时间是 2,在它进入时进程 1
占用资源,由于写者优先,它等进程 2 和 5 都释放资源后才能调用资源;
进程 4 是 R 操作,在时间 6 时进入队列,运行时间是 5,与进程 3 相同;
进程 5 是 W 操作,在时间 5.1 时进入队列,运行时间是 3,在它进入时进
程 1 占用资源,它等待资源,当 1 释放资源后,进程 2 占用资源,由于写写互
斥,它依然等待,直到进程 2 结束。
2
3
图 1 读者优先结果图
4
图 2 写者优先结果图
5
附录:程序清单及说明
1、程序读者优先:
#include
#include
#include
#include
#include
#include
#define MAX_PERSON 100
#define READER
#define WRITER
#define END
#define R
#define W
-1
READER
WRITER
0
1
typedef struct _Person
{
HANDLE m_hThread;
int m_nType;
int m_nStartTime;
int m_nWorkTime;
int m_nID;
}Person;
//最多 100 人
//读者
//写者
//结束
//定义处理线程的句柄
//进程类型(读写)
//开始时间
//运行时间
//进程号
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);
6
int main()
{
g_hReadSemaphore = CreateSemaphore(NULL,1,100,NULL);
g_hWriteSemaphore = CreateSemaphore(NULL,1,100,NULL);
//创建信号灯,当前可用的资源数为 1,最大为 100
//创建信号灯,当前可用的资源数为 1,最大为 100
CreatePersonList(g_PersonLists);
printf("Created all the reader and writer\n...\n");
g_CurrentTime = 0;
while(true)
{
// Create All the reader and writers
Sleep(300); // 300 ms
g_CurrentTime++;
printf("-----------------------------------------------------
\nCurrentTime = %d\n",g_CurrentTime);
if(finished)
{
system("pause");
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 wrong\n",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)
{
7
//读操作还没有到达执行时间,则等待
}
printf("Reader %d is Requesting ...\n",pPerson->m_nID);
//printf("\n\n************************************************\n");
// wait for the write request
/* while(g_NumOfWriteRequest != 0)
{
//g_NumOfWriteRequest != 0 表示现在正在写,不能读
}*/
//该语句在写者优先的时候是认为写者优先级高于读者,在有写者的时候读者需//要等候,
而在读者优先的时候,不用判断是否存在写者,有读者时即开始读操//作。
WaitForSingleObject(g_hReadSemaphore,INFINITE);
//等待 g_hReadSemaphore 读信号,即当 g_hReadSemaphore 有信号时等待结束 相当于 p 操作
if(g_NumOfReading ==0)
{
WaitForSingleObject(g_hWriteSemaphore,INFINITE); //当第一个读者到了,如果
g_hWriteSemaphore 信号灯灭了,说明有写者再写,读者必须等待。即互斥写操作
}
g_NumOfReading++;
ReleaseSemaphore(g_hReadSemaphore,1,NULL); //还有读者,但是允许下一个读进程读取,
相当于 V 操作// modify the reader's real start time
pPerson->m_nStartTime = g_CurrentTime;
printf("Reader %d is Reading the Shared Buffer...\n",pPerson->m_nID);
printf("\n\n************************************************\n");
while(g_CurrentTime < pPerson->m_nStartTime + pPerson->m_nWorkTime)
{
// .. perform read operations
}
printf("Reader %d is Exit...\n",pPerson->m_nID);
//printf("\n\n************************************************\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 == 5) 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("\n\n************************************************\n");
//g_NumOfWriteRequest++;
//在写者优先的时候需要用自加来初始信号值,而在读者优先的时是通过读者操作来控制信
号值