读者/写者问题与进程同步——写者优先
设计目的
理解临界区和进程互斥的概念,掌握用信号量和PV操作实现进程互斥的方法。
设计要求
在windows环境下编写一个控制台应用程序,该程序运行时能创建N个线程,其中既有读者线程又有写者线程,它们按照事先设计好的测试数据进行读写操作。请用信号量和PV操作实现读者/写者问题。
读者/写者问题的描述如下:
有一个被许多进程共享的数据区,这个数据区可以是一个文件,或者主存的一块空间,甚至可以是一组处理器寄存器。有一些只读取这个数据区的进程(reader)和一些只往数据区中写数据的进程(writer)。以下假设共享数据区是文件。这些读者和写者对数据区的操作必须满足以下条件:读—读允许;读—写互斥;写—写互斥。这些条件具体来说就是:
(1)任意多的读进程可以同时读这个文件;
(2)一次只允许一个写进程往文件中写;
(3)如果一个写进程正在往文件中写,禁止任何读进程或写进程访问文件;
(4)写进程执行写操作前,应让已有的写者或读者全部退出。这说明当有读者在读文件时不允许写者写文件。
(5)写者优先,即当有读者和写者同时等待时,首先满足写者。当一个写者声明想写文件时,不允许新的读者再访问文件。
算法设计与分析
程序结构设计:
设计测试数据
为了验证算法的正确性,需要设计测试数据,并对测试数据进行分析,总结出在该组测试数据下,程序应该得到什么结果,然后运行程序,将程序运行结果与分析结果相比较,如果二者一致,则可认为程序是正确的。
作者设计的测试数据如图9-1所示,包括10个线程,其中有5个读者线程r1~r5,另外5个是写者线程w1~w5。读者线程r1在时刻0提出读请求,如果请求得到允许,r1将用15秒的时间读文件;写者线程w3在时刻6提出写请求,如果请求得到允许,w3将用10秒的时间写文件。从表中可以看出,10个线程提出请求的次序是:r1,r2,w1,r3,w2,w3,r4,r5,w4,w5。
图9-1 测试数据 |
||
线程名称 |
申请时刻 |
持续使用时间 |
"r1" |
0 |
15 |
"r2" |
1 |
15 |
"w1" |
3 |
3 |
"r3" |
4 |
2 |
"w2" |
5 |
6 |
"w3" |
6 |
10 |
"r4" |
7 |
8 |
"r5" |
9 |
2 |
"w4" |
10 |
18 |
"w5" |
12 |
2 |
线程实际读写文件顺序为:r1,r2,w1,w2,w3,w4,w5,r3,r4,r5。执行情况见图9-2。
图9-2 线程的运行状况 |
||||
线程名称 |
申请时刻 |
持续时间 |
开始操作时刻 |
结束操作时刻 |
"r1" |
0 |
15 |
0 |
15 |
"r2" |
1 |
15 |
1 |
16 |
"w1" |
3 |
3 |
16 |
19 |
"w2" |
5 |
6 |
19 |
25 |
"w3" |
6 |
10 |
25 |
35 |
"w4" |
10 |
18 |
35 |
53 |
"w5" |
12 |
2 |
53 |
55 |
"r3" |
4 |
2 |
55 |
57 |
"r4" |
7 |
8 |
55 |
63 |
"r5" |
9 |
2 |
55 |
57 |
程序功能及界面设计
该程序采用简单的控制台应用程序界面,在主界面上显示程序的功能。
函数设计
实现读者/写者问题的源程序名称是reader_and_writer.cpp。该程序共包括4个函数。各函数及其功能如图9-3。
包括函数 |
函数功能 |
main() |
显示主菜单,接收用户的选择并执行相应的功能。 |
WF_reader_thread() WF_writer_thread() writer_first() |
写者优先算法的读者线程函数 写者优先算法的写者线程函数 写者优先算法的初始化函数:创建10个线程并等待它们结束 |
图9-3 函数功能简述 |
程序开始部分定义了宏MAX_THREAD,表示程序中创建的线程数。还定义了测试数据的结构体TEST_INFO,该结构体包含三个数据项:线程名称;提出请求的时刻;操作持续时间。接着定义了全局变量,这些全局变量的作用如下:
数组test_data保存了10个线程的测试数据;
整数read_count记录一段时间内同时对文件进行读操作的线程数;
整数write_count记录一段时间内提出写操作请求的线程数,该整数只在写者优先算法中使用;
CS_DATA是临界区变量,用来保护文件,实现对文件的读—写互斥和写—写互斥(相当于算法描述中的r_w_w);
互斥体h_mutex_read_count用来保护整数read_count,以保证多个读者对read_count的互斥访问;
互斥体h_mutex_write_count用来保护整数write_count,以保证多个写者对write_count的互斥访问,该互斥体只在写者优先算法中使用;
互斥体h_mutex_first_reader_wait和h_mutex_reader_wait只在写者优先算法中使用,当有写者在写文件时,提出读请求的第一个读者阻塞在h_mutex_first_reader_wait上,其余的读者阻塞在h_mutex_reader_wait上;
代码的话作为参考应该还是可以用的
#include#include #include #include #include #include #include <string.h> #define MAX_THREAD 10 //待测试的线程数 typedef struct{ //表示测试数据格式 char thread_name[3]; //线程名 unsigned int require_moment; //请求操作时刻 unsigned int persist_time; //操作持续时间 }TEST_INFO; TEST_INFO test_data[MAX_THREAD]={ //测试数据表 {"r1",0,15}, // r表示读者线程 {"r2",1, 15}, //w表示写者线程 {"w1",3,3}, {"r3",4, 2}, {"w2",5,6}, {"w3",6,10}, {"r4",7,8}, {"r5",9,2}, {"w4",10,18}, {"w5",12,2} }; int read_count=0; //记录正在读文件的读者数 int write_count=0; //在写者优先算法中记录声明要写文件的写者数 CRITICAL_SECTION CS_DATA; //用于保护文件的临界区变量 HANDLE h_mutex_read_count=CreateMutex(NULL,FALSE,_T("mutex_read_count"));//读者计数器互斥体 HANDLE h_mutex_write_count=CreateMutex(NULL,FALSE,_T("mutex_write_count"));//写者计数器互斥体 HANDLE h_mutex_reader_wait=CreateMutex(NULL,FALSE,_T("mutex_reader_wait"));//在写者优先算法中用于阻塞读者的互斥体 HANDLE h_mutex_first_reader_wait=CreateMutex(NULL,FALSE,_T("mutex_first_reader_wait"));//在写者优先算法中用于阻塞第一个读者的互斥体 //写者优先时的读者线程 void WF_reader_thread(void *data){ char thread_name[3]; //存放线程名称 strcpy(thread_name,((TEST_INFO *)data)->thread_name); Sleep(((TEST_INFO *)data)->require_moment*100);//模拟到达时间 WaitForSingleObject(h_mutex_reader_wait,-1); //申请读取权限 WaitForSingleObject(h_mutex_first_reader_wait,-1); WaitForSingleObject(h_mutex_read_count,-1); read_count++; if(read_count==1) EnterCriticalSection(&CS_DATA);//确保同一时间只有一个线程被操作 //释放 ReleaseMutex(h_mutex_read_count); ReleaseMutex(h_mutex_first_reader_wait); ReleaseMutex(h_mutex_reader_wait); printf("%s ",thread_name); WaitForSingleObject(h_mutex_read_count,-1); read_count--;//互斥操作 if(read_count==0)//读者操作进行完毕,释放文件资源和文件信号量 LeaveCriticalSection(&CS_DATA);//放弃对当前部分锁定权 ReleaseMutex(h_mutex_read_count); } //写者优先时的写者线程 void WF_writer_thread(void *data){ Sleep(((TEST_INFO *)data)->require_moment*100); WaitForSingleObject(h_mutex_write_count,-1);//申请写者计数器资源 if(write_count==0) //如果队列是空的就优先申请 WaitForSingleObject(h_mutex_first_reader_wait,-1);//前面读者的被申请就会被阻断,到这里变成写者的 write_count++; //(写者的插队操作) ReleaseMutex(h_mutex_write_count);//释放互斥信号量 EnterCriticalSection(&CS_DATA); printf("%s ",((TEST_INFO *)data)->thread_name); Sleep(((TEST_INFO *)data)->persist_time*100); LeaveCriticalSection(&CS_DATA); WaitForSingleObject(h_mutex_write_count,-1); write_count--; if(write_count==0) ReleaseMutex(h_mutex_first_reader_wait); ReleaseMutex(h_mutex_write_count); } //写者优先时的初始化程序 void writer_first(){ int i=0; HANDLE h_thread[MAX_THREAD]; printf("\n写优先申请次序:"); for(i=0;i ){ printf("%s ",test_data[i].thread_name); } printf("\n"); printf("写优先操作次序:"); InitializeCriticalSection(&CS_DATA); for(i=0;i ){ if(test_data[i].thread_name[0]=='r') h_thread[i]=CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)(WF_reader_thread), &test_data[i],0,NULL); else h_thread[i]=CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)(WF_writer_thread), &test_data[i],0,NULL); } WaitForMultipleObjects(MAX_THREAD,h_thread,TRUE,-1); printf("\n"); } //主程序 int main(int argc,char *argv[]){ char select; while(1){ printf("|-----------------------------------|\n"); printf("| 1:writer first |\n"); printf("| 2:exit |\n"); printf("|-----------------------------------|\n"); printf("select a function(1~2):"); do{ select=(char)getch(); }while(select!='1'&&select!='2'); //system("cls"); switch(select){ case '1': writer_first(); break; case '2': return 0; } printf("\nPress any key to continue."); getch(); //system("cls"); } return 0; }
流程图什么的就不放出来了,弄得乱七八糟的