经典同步问题之读者写者问题

经典同步问题之读者写者问题

读者写者问题中,有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间,有一些只读取这个数据区的进程(读者)和一些只往数据区写数据的进程(写者)。此外还需要满足以下条件:

  1. 任意多个读者可以同时读这个文件
  2. 一次只能有一个写者可以往文件中写(写者必须互斥)
  3. 如果一个写者正在操作,禁止任何读进程读文件和其他任何写进程写文件

读者优先算法

一个读者试图进行读操作,如果这时正有其他读者进行读操作,他可以直接开始读,而不需要等待。

读者陆续到来,读者一到就能够开始读操作,而写者进程只能等待所有读者都退出才能够进行写操作

信号量:

  1. 记录读者数量的整型变量readcount,初值为0,当值大于0时,表明有读者存在,写者不能进行写操作
  2. 互斥信号量rmutex,初值为1,用户保证多个读者进程对于readcount的互斥访问
  3. 互斥信号量dmutex,初值为1,用于控制写者进程对于数据区的互斥访问。
semaphore rmutex = 1;
semaphore dmutex = 1;
int readcount = 1;
void reader(){
    while(true){
        P(rmutex);
        if(++readcount == 1) P(dmutex);
        V(rmutex);
        //此处为读操作
        P(rmutex);
        if(--readcount == 0) V(dmutex);
        V(rmutex);
    }
}
void writer(){
    while(true){
        P(dmutex);
        // 此处为写操作
        V(dmutex);
    }
}

Linux中利用pthread库测试代码:

#include <bits/stdc++.h>
#include <time.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
using namespace std;
//semaphores
sem_t rmutex, dmutex;
int readcount;
struct Data{
    int id;
    int startTime, lastTime;
    Data(){}
    Data(int id, int sT, int lT):id(id), startTime(sT), lastTime(lT){}
};
void *Reader(void *d){
    Data* t = (Data*) d;
    int id = t->id;
    int startTime = t->startTime;
    int lastTime = t->lastTime;
    
    sleep(startTime);

    sem_wait(&rmutex);
    printf("Waiting: %d Reader\n", id);
    readcount ++;
    if(readcount == 1) sem_wait(&dmutex);
    sem_post(&rmutex);

    printf("Reading: %d Reader\n", id);
    sleep(lastTime);
    printf("Finished: %d Reader\n", id);

    sem_wait(&rmutex);
    readcount --;
    if(readcount == 0) sem_post(&dmutex);
    sem_post(&rmutex);

    pthread_exit(0);
}

void *Writer(void *d){
    Data* t = (Data*) d;
    int id = t->id;
    int startTime = t->startTime;
    int lastTime = t->lastTime;
    sleep(startTime);

    printf("Waiting: %d Writer\n", id);
    sem_wait(&dmutex);
    printf("Writing: %d Reader\n", id);
    sleep(lastTime);
    printf("Finished: %d Writer\n", id);
    sem_post(&dmutex);
    pthread_exit(0);
}

int main(){
    pthread_t tid;
    sem_init(&rmutex, 0, 1);
    sem_init(&dmutex, 0, 1);
    readcount = 0;

    int id, startTime, lastTime;    
    string role;
    while(~scanf("%d", &id)){
        cin >> role >> startTime >> lastTime;
        Data *d = new Data(id, startTime, lastTime);
        if(role == "R"){
            printf("Created: %d reader\n", id);
            pthread_create(&tid, NULL, Reader, d);
        } else if(role == "W"){
            printf("Created: %d Writer\n", id);
            pthread_create(&tid, NULL, Writer, d);
        } else puts("Invalid Inputs");
    }
    pthread_exit(0);
}
/*
1 R 1 3
2 W 2 4
3 R 3 5
4 R 4 6
5 W 5 7
*/

编译命令: g++ readfirst.cpp -o readfirst -lpthread

运行:./readfirst

image.png

2号写者在2时刻申请写,但实际情况时,直到4号读者读完,2号才开始写

写者优先算法

当写者和读者同时等待时,后续写者到达可以插队到等待的读者之前,只要等待队列中有写者,不管何时到达,都优先于读者被唤醒。

相对于读者优先,新增的信号量:

  1. 互斥信号量readable,初值为1,用于控制写者到达时可以优先读者进入临界区
  2. 整型信号量 writecount,初值为0,表示当前写者的数量
  3. 互斥信号量wmutex,初值为1,用户控制写者互斥访问writecount
semaphore dmutex = 1;
semaphore rmutex = 1;
semaphore wmutex = 1;
semaphore readable = 1;
int readcount = 0, writecount = 0;
void reader(){
    P(readable);    //检查是否有写者,若没有则占用,进行后续操作
    P(rmutex);      //占用rmutex,准备修改readcount
    if(++readcount == 1) P(dmutex); //若是第一个读者,占用数据区
    V(rmutex);      //释放rmutex
    V(readable);    //释放readable
    // 此处为读操作
    P(rmutex);      //占用rmutex
    if(--readcount == 0) V(dmutex); //若为最后一个读者,释放数据区
    V(rmutex);      //释放rmutex
}
void writer(){
    P(wmutex);      //占用wmutex,准备修改writecount
    if(++writecount == 1) P(readable);  //若为第一个写者,则占用readable,阻止后续读者进入
    V(wmutex);      //释放wmutex
    P(dmutex);      //占用dmutex,占用数据区
    // 此处为写操作
    V(dmutex);      //释放rmutex
    P(wmutex);      //占用wmutex,修改writecount
    if(--writecount == 0) P(readable);  //若是最后一个写者,则释放readable允许后续读者进入
    V(wmutex);      //释放wmutex
}

实测代码:

#include <bits/stdc++.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
using namespace std;
sem_t dmutex, rmutex, wmutex, readable;
int readcount, writecount;

struct Data{
    int id;
    int startTime, lastTime;
    Data(int id=0,int startTime=0, int lastTime = 0){
        this->id = id;
        this->startTime = startTime;
        this->lastTime = lastTime;
    }
};
void *Reader(void *d){
    Data* t = (Data*) d;
    int id = t->id;
    int startTime = t->startTime;
    int lastTime = t->lastTime;

    sleep(startTime);
    printf("Wating: %d Reader\n", id);

    sem_wait(&readable);
    sem_wait(&rmutex);
    readcount ++;
    if(readcount == 1) sem_wait(&dmutex);
    sem_post(&rmutex);
    sem_post(&readable);

    printf("Reading: %d Reader\n", id);
    sleep(lastTime);
    printf("Finished: %d Reader\n", id);

    sem_wait(&rmutex);
    if(--readcount == 0) sem_post(&dmutex);
    sem_post(&rmutex);
    
    pthread_exit(0);
}

void *Writer(void *d){
    Data* t = (Data*) d;
    int id = t->id;
    int startTime = t->startTime;
    int lastTime = t->lastTime;

    sleep(startTime);
    printf("Wating: %d Writer\n", id);

    sem_wait(&wmutex);
    if(++writecount == 1) sem_wait(&readable);
    sem_post(&wmutex);

    sem_wait(&dmutex);
    printf("Writing: %d Writer\n", id);
    sleep(lastTime);
    printf("Finished: %d Writer\n", id);
    sem_post(&dmutex);

    sem_wait(&wmutex);
    if(--writecount == 0) sem_post(&readable);
    sem_post(&wmutex);

    pthread_exit(0);
}
int main(){
    freopen("process", "r", stdin);
    pthread_t tid;
    sem_init(&rmutex, 0, 1);
    sem_init(&dmutex, 0, 1);
    sem_init(&wmutex, 0, 1);
    sem_init(&readable, 0, 1);

    readcount = writecount = 0;
    int id, startTime, lastTime;
    string role;
    while(~scanf("%d", &id)){
        cin >> role >> startTime >> lastTime;
        Data *d = new Data(id, startTime, lastTime);
        if(role == "R"){
            printf("Created: %d reader\n", id);
            pthread_create(&tid, NULL, Reader, d);
        } else if(role == "W"){
            printf("Created: %d Writer\n", id);
            pthread_create(&tid, NULL, Writer, d);
        } else puts("Invalid Inputs");
    }
    pthread_exit(0);
}
/*
1 R 1 10
2 R 2 1
3 W 3 10
4 R 14 3
5 W 15 5
*/

image.png

3号写者正在写时,4号读者先于5号读者到来,而当3号写者完成写操作时,5号写者率先一步开始写操作

参考:https://blog.csdn.net/lllllyt/article/details/80507084

posted @ 2020-05-06 13:33  kpole  阅读(661)  评论(0编辑  收藏  举报