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

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

作者: 小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;
}

相关文章

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

    问题描述:假设一个数据库为多个并发线程所共享。有的线程只读,有的线程需要写数据库。因此要求:①允许多个读者可以同时...

  • 冷月手撕408之操作系统(10)-经典同步互斥问题

    操作系统的经典同步互斥问题主要是介绍了 几个经典的同步互斥问题,其中搞懂生产者消费者问题、读者写者问题;其他的问题...

  • java同步线程(一)

    java同步线程(一) 线程安全问题,有一个经典的问题: 银行取钱问题:1、账户类 2、取钱的线程类: 3、Tes...

  • 线程的基本语法

    线程同步[解决线程安全问题] 解决线程安全问题 线程同步方式一:同步代码 语法基本 synchronized (同...

  • OpenMP多线程——Parallel for

    多线程——线程同步 数据竞争问题 线程互斥同步——critical 线程互斥同步——atmoic 线程互斥同步——...

  • 线程同步经典问题之哲学家就餐问题

    问题描述由Dijkstra提出并解决的哲学家就餐问题是典型的同步问题。该问题描述的是五个哲学家共用一张圆桌,分别坐...

  • 经典多线程同步问题详解

    前言 多线程同步问题是操作系统课程重点内容,是所有程序员解决并发问题无法绕开的一个领域,当然PHP、NodeJS例...

  • Java线程安全

    线程安全问题的概述 线程安全问题的代码实现 线程安全问题产生的原理 解决线程安全问题_同步代码块 同步代码块:sy...

  • 操作系统:信号量与管程

    第10章:信号量与管程 信号量信号量使用互斥访问条件同步管程经典同步问题哲学家就餐问题读者-写者问题 10.1 信...

  • java 线程安全问题的解决办法 和死锁

    线程安全问题的解决办法 线程 安全问题的解决方案:sun提供了线程同步机制让我们解决这类问题的。 java线程同步...

网友评论

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

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