zoukankan      html  css  js  c++  java
  • 经典同步问题之读者写者问题

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

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

    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
    ", id);
        readcount ++;
        if(readcount == 1) sem_wait(&dmutex);
        sem_post(&rmutex);
    
        printf("Reading: %d Reader
    ", id);
        sleep(lastTime);
        printf("Finished: %d Reader
    ", 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
    ", id);
        sem_wait(&dmutex);
        printf("Writing: %d Reader
    ", id);
        sleep(lastTime);
        printf("Finished: %d Writer
    ", 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
    ", id);
                pthread_create(&tid, NULL, Reader, d);
            } else if(role == "W"){
                printf("Created: %d Writer
    ", 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
    ", id);
    
        sem_wait(&readable);
        sem_wait(&rmutex);
        readcount ++;
        if(readcount == 1) sem_wait(&dmutex);
        sem_post(&rmutex);
        sem_post(&readable);
    
        printf("Reading: %d Reader
    ", id);
        sleep(lastTime);
        printf("Finished: %d Reader
    ", 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
    ", id);
    
        sem_wait(&wmutex);
        if(++writecount == 1) sem_wait(&readable);
        sem_post(&wmutex);
    
        sem_wait(&dmutex);
        printf("Writing: %d Writer
    ", id);
        sleep(lastTime);
        printf("Finished: %d Writer
    ", 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
    ", id);
                pthread_create(&tid, NULL, Reader, d);
            } else if(role == "W"){
                printf("Created: %d Writer
    ", 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

  • 相关阅读:
    【SQL跟踪工具】SQL Profiler 跟踪器
    使用Fiddler调试手机端页面请求/抓包
    SQL 常用判断语句
    VS中常用快捷键
    博客园博客自动生成目录/目录索引
    BZOJ 1135 P3488 LYZ-Ice Skates 线段树+Hall
    BZOJ 4823 老C的方块
    POJ
    BZOJ 1299 [LLH邀请赛]巧克力棒
    BZOJ 2437 [Noi2011]兔兔与蛋蛋
  • 原文地址:https://www.cnblogs.com/1625--H/p/12835837.html
Copyright © 2011-2022 走看看