211320周俊霞读者写者问题实验报告word文档良心出品.docx
- 文档编号:25551833
- 上传时间:2023-06-09
- 格式:DOCX
- 页数:34
- 大小:104KB
211320周俊霞读者写者问题实验报告word文档良心出品.docx
《211320周俊霞读者写者问题实验报告word文档良心出品.docx》由会员分享,可在线阅读,更多相关《211320周俊霞读者写者问题实验报告word文档良心出品.docx(34页珍藏版)》请在冰豆网上搜索。
211320周俊霞读者写者问题实验报告word文档良心出品
操作系统试验--读者-写者问题
周俊霞2011211320
2011211307班
进程同步
1.实习要求:
本课程实验内容引自《Windows内核实验教程》(陈向群、林斌等编著,机械工业出版社2002.9)。
在Windows环境下,创建一个包含n个线程的控制进程。
用这n个线程来表示n个读者或写者。
每个线程按相应测试数据文件的要求,进行读写操作。
请用信号量机制分别实现读者优先和写者优先的读者-写者问题。
1.读者-写者问题的读写操作限制:
1)写-写互斥;
2)读-写互斥;
3)读-读允许;
2.读者优先的附加限制:
如果一个读者申请进行读操作时已有另一读者正在进行读操作,
则该读者可直接开始读操作。
写者优先的附加限制:
如果一个读者申请进行读操作时已有另一写者在等待访问共享资
源,则该读者必须等到没有写者处于等待状态后才能开始读操作。
运行结果显示要求:
要求在每个线程创建、发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息,以确信所有处理都遵守相应的读写操作限制。
二.测试数据文件格式
测试数据文件包括n行测试数据,分别描述创建的n个线程是读者还是写者,以及读写
操作的开始时间和持续时间。
每行测试数据包括四个字段,各字段间用空格分隔。
第一字段
为一个正整数,表示线程序号。
第一字段表示相应线程角色,R表示读者是,W表示写者。
第二字段为一个正数,表示读写操作的开始时间。
线程创建后,延时相应时间(单位为秒)
后发出对共享资源的读写申请。
第三字段为一个正数,表示读写操作的持续时间。
当线程读
写申请成功后,开始对共享资源的读写操作,该操作持续相应时间后结束,并释放共享资源。
下面是一个测试数据文件的例子:
1R35
2W45
3R52
4R65
5W5.13
三、与实验相关的API介绍
在本实验中可能涉及的API有:
线程控制:
CreateThread完成线程创建,在调用进程的地址空间上创建一个线程,以执行指定的函
数;它的返回值为所创建线程的句柄。
HANDLECreateThread(
LPSECURITY_ATTRIBUTESlpThreadAttributes,//SD
DWORDdwStackSize,//initialstacksize
LPTHREAD_START_ROUTINElpStartAddress,//thread
function
LPVOIDlpParameter,//threadargument
DWORDdwCreationFlags,//creationoption
LPDWORDlpThreadId//threadidentifier
);
ExitThread用于结束当前线程。
VOIDExitThread(
DWORDdwExitCode//exitcodeforthisthread
);
Sleep可在指定的时间内挂起当前线程。
VOIDSleep(
DWORDdwMilliseconds//sleeptime
);
信号量控制:
CreateMutex创建一个互斥对象,返回对象句柄;
HANDLECreateMutex(
LPSECURITY_ATTRIBUTESlpMutexAttributes,//SD
BOOLbInitialOwner,//initialowner
LPCTSTRlpName//objectname
);
OpenMutex打开并返回一个已存在的互斥对象句柄,用于后续访问;
HANDLEOpenMutex(
DWORDdwDesiredAccess,//access
BOOLbInheritHandle,//inheritanceoption
LPCTSTRlpName//objectname
);
ReleaseMutex释放对互斥对象的占用,使之成为可用。
BOOLReleaseMutex(
HANDLEhMutex//handletomutex
);
WaitForSingleObject可在指定的时间内等待指定对象为可用状态;
DWORDWaitForSingleObject(
HANDLEhHandle,//handletoobject
DWORDdwMilliseconds//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,//initialstacksize
LPTHREAD_START_ROUTINElpStartAddress,//threadfunction
LPVOIDlpParameter,//threadargument
DWORDdwCreationFlags,//creationoption
LPDWORDlpThreadId//threadidentifier);
ExitThread用于结束当前线程。
VOIDExitThread(DWORDdwExitCode//exitcodeforthisthread);
Sleep可在指定的时间内挂起当前线程。
VOIDSleep(DWORDdwMilliseconds//sleeptime);
信号量控制:
CreateMutex创建一个互斥对象,返回对象句柄;
HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes,
BOOLbInitialOwner,//initialowner
LPCTSTRlpName//objectname);
OpenMutex打开并返回一个已存在的互斥对象句柄,用于后续访问;
HANDLEOpenMutex(DWORDdwDesiredAccess,//access
BOOLbInheritHandle,//inheritanceoption
LPCTSTRlpName//objectname);
ReleaseMutex相当于V操作,释放对互斥对象的占用,使之成为可用。
BOOLReleaseMutex(HANDLEhMutex//handletomutex);
WaitForSingleObject相当于P操作,可在指定的时间内等待指定对象为可用状态;
DWORDWaitForSingleObject(HANDLEhHandle,//handletoobject
DWORDdwMilliseconds//time-outinterval);
EnterCriticalSection
LeaveCriticalSection
四、实验实例:
1R35
2W45
3R52
4R65
5W5.13
1.读优先:
只要有一个读者正在读,那么后续的读者都能立即读,不管有多少写者在等待。
可能导致写者饥饿。
1.读者
1)写者写时,不可读
2)有别的读者正在读,可读
2.写者
1)有读者正在读,不可写
2)有写者正在写,不可写
3)无读者正在读,无写者正在写,可写
运行结果正确:
1R35
2W45
3R52
4R65
5W5.13
读者优先:
依次为:
读者1发送读请求;
读者1开始读操作;
写者2发送写请求;
读者3发送读请求;
读者3开始读操作;
写者5发送写请求;
读者4发送读请求;
读者4开始读操作;
读者3完成了读操作;
读者1完成了读操作;
读者4完成了读操作;
写者2开始写操作;
写者2完成写操作;
写者5开始写操作;
写者5完成写操作;
2.写优先:
1R35
2W45
3R52
4R65
5W5.13
当新的写者希望写时,不允许该写者后续的读者访问数据区,但必须保证之前的读者读完。
1.读者特点
1)有写者正在写或者等待写,须等到没有写者才能读
2)没有写者,可以读
2.写者特点
1)写者与写者互斥。
当其它写者正在写时,其它写者不能写。
2)写者与读者互斥。
之前只有读者在读,当写者出现时,必须等到之前的读者都读完才能写。
这尊重了之前读者的意愿。
3)写者可以有条件地插读者的队。
当前有写者正写,有读者在等,这时来了新写者,新写者可以在那些读者之前执行。
3.写者实现
事实上,这个写者优先并没有成功实现。
。
。
只是学到了一些知识。
。
。
。
。
首先,写者的代码应该是这样一种形式,才能保证同一时刻只有一个写者修改数据:
[cpp]viewplaincopy
1while
(1)
2{
3pthread_mutex_lock(&writeLock);
4{//临界区,限制只有一个写者修改数据
5write();
6}
7pthread_mutex_unlock(&writeLock);
8}
考虑到写者对读者的影响是:
当任何写者想写时,读者都必须被阻塞;并且,写者阻塞了读者并停止阻塞之前,后续的任何写者都会优先于读者执行。
这就如同有一个写者队列,当第一个写者入队时,读者完全被阻塞,直到最后一个写者离开队列。
据此,可以用writerCnt来统计写者的数量,而用互斥量accessWriterCnt来互斥各线程对writerCnt的访问。
代码作如下调整:
[cpp]viewplaincopy
1while
(1)
2{
3pthread_mutex_lock(&accessWriterCnt);
4{//临界区,希望修改writerCnt,独占writerCnt
5writerCnt++;
6if(writerCnt==1){
7//读者与写者互斥;使用readerLock来描述;
8pthread_mutex_lock(&readerLock);
9}
10}
11pthread_mutex_unlock(&accessWriterCnt);
12
13
14pthread_mutex_lock(&writeLock);
15{//临界区,限制只有一个写者修改数据
16write();
17}
18pthread_mutex_unlock(&writeLock);
19
20pthread_mutex_lock(&accessWriterCnt);
21{//临界区,希望修改writerCnt,独占writerCnt
22writerCnt--;
23if(writerCnt==0){
24//阻止后续的读者加入待读队列
25pthread_mutex_unlock(&readerLock);
26}
27}
28pthread_mutex_unlock(&accessWriterCnt);
29sleep(W_SLEEP);
30}
4.读者的实现
读者的实现首先必须保证支持多个读者同时读,因此下面的代码不合适:
[cpp]viewplaincopy
1while
(1)
2{
3pthread_mutex_lock(&readerLock);
4{//临界区
5read();
6}
7pthread_mutex_unlock(&readerLock);
8}
在上面的实现中,很明显的问题是,当一个读者执行read()的同时,不可能有另外的读者进入临界区并执行read()函数了。
因此,必须确保在执行read()函数之前对readerLock解锁。
代码类似于:
[cpp]viewplaincopy
9while
(1)
10{
11pthread_mutex_lock(&readerLock);
12pthread_mutex_unlock(&readerLock);
13read();
14}
于是乎,现在我们可以注意到,假如写者率先锁定了readerLock,那么后续的读者被阻塞在pthread_mutex_lock(&readerLock);了,这点很正确。
在写者最终释放readerLock之前,可能有成千上万的读者都被阻塞在readerLock上,readerLock释放之后,这些读者会竞争这readerLock,嗯,这没什么问题。
问题在于,此后,将可能有新的写者有写需求并希望获得这把锁(参照一下写者代码吧),那么,此时,写者不得不和成千上万的读者一起竞争readerLock,由于将占有readerLock的人是随机的,写者在数量上不占优势,将进入饥饿状态。
因此,这种实现无法满足“写者优先”的需求。
通过添加另一个互斥量,我们可以把写者的这些成千上万的竞争者不再竞争readerLock,下面的方案里,当读者释放锁时,假如写者想获得这把锁,那么它能立即得到。
[cpp]viewplaincopy
1while
(1)
2{
3pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里
4pthread_mutex_lock(&readerLock);//只被一个读者占有
5
6pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
7pthread_mutex_unlock(&outerLock);
8read();
9}
为了防止执行read()的同时,写者正在执行write(),所以,有必要在读者的代码中,等待write()函数执行完,注意到写者的代码中,使用writeLock互斥量来保护数据:
[cpp]viewplaincopy
10pthread_mutex_lock(&writeLock);
11{//临界区,限制只有一个写者修改数据
12write();
13}
14pthread_mutex_unlock(&writeLock);
因而,读者也可以通过对writeLock加锁以保证读的时候没有同时写,但是下面这种做法又导致读者无法并发:
[cpp]viewplaincopy
15while
(1)
16{
17pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里
18pthread_mutex_lock(&readerLock);//只被一个读者占有
19
20pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
21pthread_mutex_unlock(&outerLock);
22
23pthread_mutex_lock(&writeLock);//每个执行到这里的读者都企图对writeLock加锁
24{//临界区
25read();//同一时刻只能有一个读者在这里,无法实现读并发
26}
27pthread_mutex_unlock(&writeLock);
28}
我们需要更合理地安排lock(&writeLock)的位置,并且需要避免每个读者都对writeLock加锁,即,只让某个读者对writeLock加锁,同时还保证读的时候,没有写者在写。
注意到,假如有多个读者并发地执行read()函数,那么,只要在第一个读者开始读之前锁住writeLock在最后一个读者执行read()之后解锁writeLock,就可以解决读者并发的问题。
我们需要为读者添加两段代码:
代码段1:
[cpp]viewplaincopy
29pthread_mutex_lock(&accessReaderCnt);
30{//临界区
31readerCnt++;
32if(readerCnt==1){//第一个并发读线程
33pthread_mutex_lock(&writeLock);//在第一个并发读者这里开始禁止写者执行写操作
34}
35}
36pthread_mutex_unlock(&accessReaderCnt);
代码段2:
[cpp]viewplaincopy
37pthread_mutex_lock(&accessReaderCnt);
38{//临界区
39readerCnt--;
40if(readerCnt==0){//最后一个并发读线程
41pthread_mutex_unlock(&writeLock);//在最后一个并发读者read()结束后写者可以执行写操作
42}
43}
44pthread_mutex_unlock(&accessReaderCnt);
代码段2明显应该放在read()函数之后;
[cpp]viewplaincopy
45while
(1)
46{
47
48//代码段1可能的位置0
49pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里
50//代码段1可能的位置1
51pthread_mutex_lock(&readerLock);//只被一个读者占有
52//代码段1可能的位置2
53pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
54//代码段1可能的位置3
55pthread_mutex_unlock(&outerLock);
56//代码段1可能的位置4
57
58read();
59pthread_mutex_lock(&accessReaderCnt);//代码段2
60{//临界区
61readerCnt--;
62if(readerCnt==0){
63pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作
64}
65}
66pthread_mutex_unlock(&accessReaderCnt);
67}
经典的做法是把代码段1放到位置2,如果是这样,那么代码如下,我故意用了很多块作用域(用花括号{}表示)来凸显临界区:
[cpp]viewplaincopy
68while
(1)
69{
70pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里
71{//临界区
72pthread_mutex_lock(&readerLock);//只被一个读者占有
73{//临界区
74pthread_mutex_lock(&accessReaderCnt);//代码段1
75{//临界区
76readerCnt++;
77if(readerCnt==1){
78pthread_mutex_lo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 211320 周俊霞 读者 问题 实验 报告 word 文档 良心 出品