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

2019-10-31  本文已影响0人  小pb

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

读者优先:

这部分的讲解参考:(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;
}
上一篇下一篇

猜你喜欢

热点阅读