读者/写者问题与进程同步——写者优先


设计目的

  理解临界区和进程互斥的概念,掌握用信号量和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;
}

流程图什么的就不放出来了,弄得乱七八糟的

相关