美文网首页
线程同步经典问题之读者-作者问题

线程同步经典问题之读者-作者问题

作者: 小pb | 来源:发表于2019-10-31 21:53 被阅读0次

    问题描述:
    假设一个数据库为多个并发线程所共享。有的线程只读,有的线程需要写数据库。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。
    这个问题有两个变种:
    读者优先(第一读者-作者): 要求读者不应该保持等待,除非作者已经获得了权限使用对象。
    写者优先(第二读者-作者): 要求一旦作者就绪,就应该尽快的执行,其他读者应该进行等待。
    两种问题都会带来了饥饿
    第一种写者饥饿;第二种读者饥饿。

    读者优先:

    • 关系分析。
      由题目分析读者和写者是互斥的,写者和写者也是互斥的,而读者和读者不存在互斥问题。

    • 整理思路。
      两个进程,即读者和写者。写者是比较简单的,它和任何进程互斥,它写入的数据部分应该属于临界区。用互斥信号量的P操作、V操作即可解决。

      读者的问题比较复杂,它必须实现与写者互斥的同时还要实现与其他读者的同步,因此,仅仅简单的一对P操作、V操作是无法解决的。
      那么,在这里用到了一个计数器,用它来判断当前是否有读者读文件。当有读者的时候写者是无法写文件的,此时读者会一直占用文件,当没有读者的时候写者才可以写文件。同时这里不同读者对计数器的访问也应该是互斥的。

    • 信号量设置。
      首先设置信号量count为计数器,用来记录当前读者数量,初值为0;
      设置mutex为互斥信号量,用于保护更新count变量时的互斥;
      设置互斥信号量rw用于保证读者和写者的互斥访问

    这部分的讲解参考:(https://blog.csdn.net/u014253011/article/details/82744241
    )

    int count=0;  //用于记录当前的读者数量
    semaphore mutex=1;  //用于保护更新count变量时的互斥
    semaphore rw=1;  //用于保证读者和写者互斥地访问文件
    writer () {  //写者进程
        while (1){
            P(rw); // 互斥访问共享文件
            Writing;  //写入
            V(rw) ;  //释放共享文件
        }
    }
    
    读者:作为读者,当你去拿一个资源时,你首先要看read_count是多少,如果是0,表示这个资源有可能被写进程占用,先用写锁将其锁住,避免写进程修改进来。当读完了,需要看,自己自己读完以后,是否是count 是0, 因为前面对 写进程进行了加锁,如果是0,需要释放让写进程可以拿到这个数据,
    
    reader () {  // 读者进程
        while(1){
            P (mutex) ;  //互斥访问count变量
            if (count==0)  //当第一个读进程读共享文件时
                P(rw);  //阻止写进程写
            count++;  //读者计数器加1
            V (mutex) ;  //释放互斥变量count
            reading;  //读取
            P (mutex) ;  //互斥访问count变量
            count--; //读者计数器减1
            if (count==0)  //当最后一个读进程读完共享文件
                V(rw) ;  //允许写进程写
            V (mutex) ;  //释放互斥变量 count
        }
    }
    

    写者优先

    在读者在运行时,有写请求进来,应该禁止后面的所有的读请求,而写请求进行到排队中,直到写请求执行完了,再次执行下一个读请求。
    为此,增加一个信号量并且在上面的程序中 writer()和reader()函数中各增加一对PV操作,就可以得到写进程优先的解决程序。

    int count = 0;  //用于记录当前的读者数量
    semaphore mutex = 1;  //用于保护更新count变量时的互斥
    semaphore rw=1;  //用于保证读者和写者互斥地访问文件
    semaphore w=1;  //用于实现“写优先”
     
    writer(){
        while(1){
            P(w);  //在无写进程请求时进入
            P(rw);  //互斥访问共享文件
            writing;  //写入
            V(rw);  // 释放共享文件
            V(w) ;  //恢复对共享支件的访问
        }
    }
     
    reader () {  //读者进程
        while (1){
            P (w) ;  // 在无写进程请求时进入
            P (mutex);  // 互斥访问count变量
     
            if (count==0)  //当第一个读进程读共享文件时
                P(rw);  //阻止写进程写
     
            count++;  //读者计数器加1
            V (mutex) ;  //释放互斥变量count
            V(w);  //恢复对共享文件的访问
            reading;  //读取
            P (mutex) ; //互斥访问count变量
            count--;  //读者计数器减1
     
            if (count==0)  //当最后一个读进程读完共享文件
                V(rw);  //允许写进程写
     
            V (mutex);  //释放互斥变量count
        }
    }
    

    下面用两种方法解决下面的读写问题:

    信号量解决

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <semaphore.h>
    
    int data = 1;
    int count = 0;
    sem_t rw_lock;
    //  读者优先的情况下只需要两个信号量,但是会产生写线程饥饿。
    //  写者优先需要三个信号量
    // sem_t r_lock;
    sem_t mutex;
    
    // 读者优先
    void* reader(void* arg) {
      while (true) {
        //  写者优先时需要打开
        //  sem_wait(&r_lock);
        sem_wait(&mutex);
        if (count == 0) {
          sem_wait(&rw_lock);
        }
        count++;
        printf("Reader has %d readers\n", count);
        sem_post(&mutex);
        printf("Reader data:%d\n", data);
        sleep(1);
        sem_wait(&mutex);
        count--;
        if (count == 0) {
          sem_post(&rw_lock);
        }
        sem_post(&mutex);
        sleep(5);
    //  写者优先时,需要打开
    //    sem_post(&r_lock);
      }
    }
    
    void* writer(void* arg) {
      while (true) {
        sem_wait(&rw_lock);
        int tmp = data;
        data = rand()% 100;
        printf("writer modify data from %d to %d.\n", tmp, data);
        sem_post(&rw_lock);
        sleep(1);
      }
    }
    
    int main(int argc, char* argv[]) {
      sem_init(&rw_lock, 0, 1);
      // 写者优先时需要打开
      //sem_init(&r_lock, 0, 1);
      sem_init(&mutex, 0, 1);
    
      srand(time(NULL));
      data = rand()% 100;
    
      pthread_t p_reader[10];
      pthread_t p_writer[3];
      for (int i = 0; i < 3; ++i) {
        pthread_create(&p_writer[i], NULL, writer, NULL);
      }
    
      for (int i = 0; i < 10; ++i) {
        pthread_create(&p_reader[i], NULL, reader, NULL);
      }
    
       for (int i = 0; i < 3; ++i) {
        pthread_join(p_writer[i], NULL);
      }
    
      for (int i = 0; i < 10; ++i) {
        pthread_join(p_reader[i], NULL);
      }
    
      sem_destroy(&mutex);
      sem_destroy(&rw_lock);
    // 写者优先时,打开
    //  sem_destroy(&r_lock);
    
      return 0;
    }
    

    读写锁解决

    LINUX 读写锁是写者优先

    #include<stdio.h>
    #include<pthread.h>
    #include<string.h>
    #include<stdlib.h>
    #include <unistd.h>
    
    pthread_rwlock_t rwlock;
    
    int good = 0;
    
    void *writer(void *argv) {
      while(true) {
        // 加上写锁
        pthread_rwlock_wrlock(&rwlock);
        good = rand() % 1000;
        printf("%u write:%d\n",(unsigned int)pthread_self() % 100,good);
        pthread_rwlock_unlock(&rwlock);
        sleep(rand()%2);
      }
      return NULL;
    }
    
    void *reader(void *argv) {
      while(true) {
        // 加上读锁
        pthread_rwlock_rdlock(&rwlock);
        printf("%u read:%d\n",(unsigned int)pthread_self() % 100,good);
        pthread_rwlock_unlock(&rwlock);
        sleep(rand()%2);
      }
      return NULL;
    }
    
    int main() {
      pthread_t pt[8];
      int i;
      // 初始化读写锁
      pthread_rwlock_init(&rwlock,NULL);
      // 创建三个写线程
      for(i=0;i < 3;i++) {
        pthread_create(&pt[i],NULL,writer,NULL);
      }
    
      // 5个读线程
      for(;i<8;i++)
        pthread_create(&pt[i],NULL,reader,NULL);
    
      for(i=0;i<8;i++)
        pthread_join(pt[i],NULL);
      pthread_rwlock_destroy(&rwlock);
      return 0;
    }
    

    相关文章

      网友评论

          本文标题:线程同步经典问题之读者-作者问题

          本文链接:https://www.haomeiwen.com/subject/xmcxbctx.html