进程同步互斥——读者写者问题
2012-12-14 14:44
441 查看
经过几天的尝试与努力,初步实现了操作系统实验设计三个题目中最简单的一个:编程模拟进程的同步与互斥。真是煞费苦心。没有可视化的操作界面,完全用C/C++来进行模拟,着实难受。
在自己对同步互斥的理解的基础上,借鉴别人的一些实现方法,终于将代码初步敲打出来。代码不多,却也复杂。下面简单对程序做下介绍。
(一)实验目的
进一步理解 “临界资源” 的概念;
把握在多个进程并发执行过程中对临界资源访问时的必要约束条件;
理解操作系统原理中 “互斥” 和 “同步” 的涵义。
(二)实验内容
利用程序设计语言编程,模拟并发执行进程的同步与互斥(要求:进程数目不少于 3 个)。
(三)、程序分析
读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:
(1) 任意多个读进程可以同时读这个文件;
(2) 一次只有一个写进程可以往文件中写;
(3) 如果一个写进程正在进行操作,禁止任何读进程度文件。
实验要求用信号量来实现读者写者问题的调度算法。实验提供了signal类,该类通过P( )、V( )两个方法实现了P、V原语的功能。实验的任务是修改Creat_Writer()添加写者进程,Creat_Reader()创建读者进程。Reader_goon()读者进程运行函数。
Ø
读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。
读者优先的附加限制:如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作。
Ø
写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。
写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。
在Windows 2000/XP 环境下,创建一个控制台进程,此进程包含 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
(原文链接:http://www.oschina.net/code/snippet_180916_7505)
在自己对同步互斥的理解的基础上,借鉴别人的一些实现方法,终于将代码初步敲打出来。代码不多,却也复杂。下面简单对程序做下介绍。
(一)实验目的
进一步理解 “临界资源” 的概念;
把握在多个进程并发执行过程中对临界资源访问时的必要约束条件;
理解操作系统原理中 “互斥” 和 “同步” 的涵义。
(二)实验内容
利用程序设计语言编程,模拟并发执行进程的同步与互斥(要求:进程数目不少于 3 个)。
(三)、程序分析
读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:
(1) 任意多个读进程可以同时读这个文件;
(2) 一次只有一个写进程可以往文件中写;
(3) 如果一个写进程正在进行操作,禁止任何读进程度文件。
实验要求用信号量来实现读者写者问题的调度算法。实验提供了signal类,该类通过P( )、V( )两个方法实现了P、V原语的功能。实验的任务是修改Creat_Writer()添加写者进程,Creat_Reader()创建读者进程。Reader_goon()读者进程运行函数。
Ø
读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。
读者优先的附加限制:如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作。
Ø
写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。
写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。
在Windows 2000/XP 环境下,创建一个控制台进程,此进程包含 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(); }; ////////////////////////////////////////////////////////////////////
[C/C++]代码
#include<windows.h> #include<fstream> #include<cstdlib> #include<iostream> 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 for writing.\n",thread_info[threadNum-1].num); thread[threadNum-1] = CreateThread(NULL, 0,writer, &thread_info[threadNum-1].num,0,0); break; case 'R': printf("Creating Thread %d for reading.\n",thread_info[threadNum-1].num); thread[threadNum-1] = CreateThread(NULL, 0,reader, &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 ) { P(rdcntmutex); if ( readcount==0) P(fmutex); readcount = readcount + 1 ; V(rdcntmutex); // Do read operation P(rdcntmutex); readcount = readcount - 1 ; if ( readcount==0) V(fmutex); V(rdcntmutex); } } void writer() { while ( 1 ) { P(fmutex); // Do write operation V(fmutex); } }
高手写法
semaphore fmutex = 1 , rdcntmutex = 1 , wtcntmutex = 1 , queue = 1 ; // fmutex --> access to file; rdcntmutex --> access to readcount // wtcntmutex --> access to writecount int readcount = 0 ,writecount = 0 ; void reader() { while ( 1 ) { P(queue);//申请队列信号 P(rdcntmutex);//修改readcount,互斥 if ( readcount==0) P(fmutex);//access to file 互斥 readcount = readcount + 1 ; V(rdcntmutex);//释放 V(queue);//释放 // Do read operation P(rdcntmutex); readcount = readcount - 1 ; 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); } }
(原文链接:http://www.oschina.net/code/snippet_180916_7505)
相关文章推荐
- 进程、线程知识点总结和同步(消费者生产者,读者写者三类问题)、互斥、异步、并发、并行、死锁、活锁的总结
- 操作系统进程同步互斥经典问题之读者写者问题
- 进程、线程知识点总结和同步(消费者生产者,读者写者三类问题)、互斥、异步、并发、并行、死锁、活锁的总结
- Java进程间的同步与互斥实例(实现读者写者问题)
- 经典进程同步问题:读者-写者问题
- 进程(线程)间的同步互斥问题(五) 读者-作者问题
- 经典进程同步问题(二)——读者、写者问题
- 进程同步的经典问题1——读者写者问题(写者优先与公平竞争)
- 进程同步的经典问题1——读者写者问题(写者优先与公平竞争)
- 操作系统同步互斥经典问题——读者写者问题
- 经典进程同步问题:读者-写者问题
- 操作系统同步互斥经典问题——读者写者问题
- 经典同步互斥问题之读者—写者问题
- Linux 关于读者与写者同步互斥问题的解析
- 进程同步的经典问题1——读者写者问题(写者优先与公平竞争)
- 操作系统清华大学版笔记(十) 信号量、管程、条件互斥、经典同步问题(读者写者、哲学家问题)
- 进程同步问题(1)——生产者,消费者 & 读者,写者问题
- 操作系统进程同步三大问题:生产者消费者,哲学家进餐,读者写者问题
- 进程(线程)间同步互斥问题(三) 熟睡的理发师问题
- 读者与写者同步问题