读者写者问题

发布时间 2023-10-02 20:37:09作者: cerwang

读者-写者问题

读者写者问题是并发和同步领域的经典问题,然而各大教科书和网络资源基本都只讲解了其中的一种——读者优先的情况,对其余情况涉及很少。本着深入研究问题的态度,在此对各种情况讨论并给出代码,仅供参考。

读者优先

这是os教科书里基本都会给出的情况,即

  • 写者必须等待所有读者读完才可以写
  • 多个读者可以同时读取,只要此时写者未获得权限

这个问题包含了读写互斥、写写互斥、允许多个读线程同时读取的意思。
此问题需要两个信号量,兼有加锁互斥和同步的功能。
读写线程通过writer_mutex实现互斥,但读线程的reader_mutex仅作为锁用来保证对readcount的修改正确。只要有一个读线程进入reading区,写者就被阻塞,而其余读者仍可以通过持有并释放reader_mutex来进入reading区,直到读者数为0,此时唤醒写者;而只要写者进入writing区,任意读者都会被阻塞(其中一个在P(&writer_mutex),其余在P(&reader_mutex))。

sem_t writer_mutex;
sem_t reader_mutex;
void* reader(){
    while(1){
        P(&reader_mutex);
        readcount++;
        if(readcount==1)
        {
            P(&writer_mutex);
        }
        V(&reader_mutex);

        // reading is perfomanced
        printf("how many readers:%d\n",readcount);

        P(&reader_mutex);
        readcount--;
        if(readcount==0)
        {
            V(&writer_mutex);
        }
        V(&reader_mutex);
    }
}
void* writer(){
    while(1){
        P(&writer_mutex);
        writecount++;

        // writing is perfomanced
        printf("how many writers:%d\n",writecount);
        
        writecount--;
        V(&writer_mutex);
    }
}

总体上来看这部分代码实现了所需功能,但也带来了缺陷:写者只能有一个且需要等待所有读者读完才能写,而只要有一个读者进入,读者就可以不受限制地进入,结果导致写进程饥饿。

写者优先

写者优先要求解决上面的饥饿问题,要求只要有写者在写,后续写者都可以写,读者必须等所有写者写完才能读。
需要五个信号量,mutex用于为写者计数器加锁,reader_mutex用于为读者计数器加锁,writer_mutex实现写者互斥,queue实现读线程排队
每个读线程都要获取queue,之后在真正做读操作前即让出(使得写线程可以获得queue)。写进程获得queue之后就一直占着,直到所有写线程都完成,提高了写线程优先级

sem_t mutex,queue;
sem_t writer_mutex;
sem_t reader_mutex;
void reader(){
    while(1){
        P(&queue);
        P(&reader_mutex);
        readcount++;
        if(readcount==1)
        {
            P(&writer_mutex);
        }
        V(&reader_mutex);
        V(&queue);

        printf("how many readers:%d\n",readcount);

        P(&reader_mutex);
        readcount--;
        if(readcount==0)
        {
            V(&writer_mutex);
        }
        V(&reader_mutex);
    }
}
void writer(){
    while(1){
        P(&mutex);
        writecount++;
        if(writecount==1){
            P(&queue);
        }
        V(&mutex);

        P(&writer_mutex);
        printf("how many readers:%d\n",readcount);
        V(&writer_mutex);

        P(&mutex);
        writecount--;
        if(writecount==0){
            V(&queue);
        }
        V(&mutex);

    }
}

公平竞争

二者都在queue上排队,即使A类线程有多个同时进入,B类线程也可以获得queue而使A类其他线程阻塞,不会像前两种那样一但其中一类线程获得优势后另一类线程很难再进入(读优先里读者获得writer_mutex后,后续读者可以不断进入导致写者很难获得writer_mutex;写优先里写者获得queue后,写者可以不断进入导致读者很难获得queue)

sem_t queue;
sem_t writer_mutex;
sem_t reader_mutex;
void* reader(){
    while(1){
        P(&queue);
        P(&reader_mutex);
        readcount++;
        if(readcount==1)
        {
            P(&writer_mutex);
        }
        V(&reader_mutex);
        V(&queue);

        printf("how many readers:%d, how many writers:%d\n",readcount,writecount);

        P(&reader_mutex);
        readcount--;
        if(readcount==0)
        {
            V(&writer_mutex);
        }
        V(&reader_mutex);
    }
}
void* writer(){
    while(1){
        P(&queue);
        P(&writer_mutex);
        writecount++;
        printf("how many readers:%d, how many writers:%d\n",readcount,writecount);
        writecount--;
        V(&writer_mutex);
        V(&queue);
    }
}

参考链接: OS | 读者写者问题(读者优先,写者优先 ,读写公平)