读者写者模型

169 阅读4分钟

读者写者模型

1、读写者模型

问题描述

在一个系统中,存在多个并发的进程或线程,这些进程或线程可以分为两类:读者(Readers)和写者(Writers)。读者只对共享资源进行读取操作,而写者则对共享资源进行写入操作。该问题需要解决以下几个关键问题:

  • 互斥访问:写者与写者之间、写者与读者之间必须互斥访问共享资源,以避免数据不一致的问题。
  • 并发读取:多个读者可以同时访问共享资源,以提高系统的并发性能。

解决方案

1. 读者优先

读者优先策略允许只要有读者正在访问共享资源,后续的读者可以直接进入,而写者必须等待所有读者离开后才能访问。这种策略的优点是可以提高读取操作的并发性能,但可能会导致写者饥饿,即写者长时间无法获得访问权。

2. 写者优先

写者优先策略确保当有写者等待时,后续的读者必须等待写者完成操作后才能进入,以避免写者长时间等待。但这种策略可能会导致读者饥饿

3. 公平策略

公平策略试图平衡读者和写者的访问权,避免饥饿现象的发生。例如,可以使用一个队列来记录等待的读者和写者,按照到达的顺序依次分配访问权。

2、使用PV原语解释读写者模型

1. 读者优先

RW1.jpg

实现了基本的读写互斥,但其他的条件如只要有读者正在访问共享资源,后续的读者可以直接进入没有实现,我们需要对模型进行一些修改。

RW2.jpg

这里实现了读者优先的功能,其中针对cnt自增自减操作也需要互斥。

Wmutex没必要,Mutex本身就可以实现写者互斥。

2. 写者优先

类似读者优先

3. 读写公平

RWE.jpg

这个会根据读者写者进入顺序来进行。

3、代码实现读写者模型

1. 读者优先

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int share_data = 0;
int cnt = 0;

pthread_mutex_t Mutex;
pthread_mutex_t Rmutex;
pthread_mutex_t Wmutex;

void *Reader(void *args) { //线程编号作为参数
    int number = *(int *)args;

        pthread_mutex_lock(&Rmutex);
        if (cnt == 0) {
            pthread_mutex_lock(&Mutex);
        }
        cnt++;
        pthread_mutex_unlock(&Rmutex);

        printf("读者 %d 正在阅读: %d\n", number, share_data);

        pthread_mutex_lock(&Rmutex);
        cnt--;
        if (cnt == 0) {
            pthread_mutex_unlock(&Mutex);
        }
        pthread_mutex_unlock(&Rmutex);
        sleep(2);

    return NULL;
}

void *Writer(void* args) {
    int number = *(int*) args;

        pthread_mutex_lock(&Mutex);
        pthread_mutex_lock(&Wmutex);
        share_data++;
        printf("写者 %d 在写 %d\n", number, share_data);
        sleep(2);
        pthread_mutex_unlock(&Wmutex);
        pthread_mutex_unlock(&Mutex);

    return NULL;
}

int main() {
    pthread_mutex_init(&Mutex, NULL);
    pthread_mutex_init(&Rmutex, NULL);
    pthread_mutex_init(&Wmutex, NULL);

    pthread_t readers[5], writers[3];
    int reader_ids[5], writer_ids[3];

    for (int i = 0; i < 5; i++) {
        reader_ids[i] = i;
        pthread_create(&readers[i], NULL, Reader, &reader_ids[i]);
    }

    for (int i = 0; i < 3; i++) {
        writer_ids[i] = i;
        pthread_create(&writers[i], NULL, Writer, &writer_ids[i]);
    }

    for (int i = 0; i < 5; i++) {
        pthread_join(readers[i], NULL);
    }

    for (int i = 0; i < 3; i++) {
        pthread_join(writers[i], NULL);
    }

    pthread_mutex_destroy(&Mutex);
    pthread_mutex_destroy(&Rmutex);
    pthread_mutex_destroy(&Wmutex);

    return 0;
}

RWcmd&res.png

值得注意的一点,线程的调度具有不确定性。即使是相同的顺序,代码运行的结果也可能不同。

RWstrange.png

2. 写者优先

语读者优先类似

3. 读写公平

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 共享资源
int shared_data = 0;
// 互斥锁
pthread_mutex_t Mutex;
pthread_mutex_t W;
pthread_mutex_t RW;

int cnt = 0;

// 读者线程函数
void* reader(void* arg) {
    int id = *(int*)arg;
    // 进入临界区
    pthread_mutex_lock(&W);
    pthread_mutex_lock(&Mutex);
    if(cnt == 0) pthread_mutex_lock(&RW);
    cnt++;
    pthread_mutex_unlock(&Mutex);
    pthread_mutex_unlock(&W);
    
    // 读取共享资源
    printf("Reader %d is reading: %d\n", id, shared_data);
    sleep(1);

    // 离开临界区
    pthread_mutex_lock(&Mutex);
    cnt--;
    if (cnt == 0) pthread_mutex_unlock(&RW);
    pthread_mutex_unlock(&Mutex);

    return NULL;
}

// 写者线程函数
void* writer(void* arg) {
    int id = *(int*)arg;
    // 进入临界区
    pthread_mutex_lock(&W);
    pthread_mutex_lock(&RW);
    // 写入共享资源
    shared_data++;
    printf("Writer %d is writing: %d\n", id, shared_data);
    sleep(1);
    pthread_mutex_unlock(&RW);
    pthread_mutex_unlock(&W);

    return NULL;
}

int main() {
    // 初始化互斥锁
    pthread_mutex_init(&Mutex, NULL);
    pthread_mutex_init(&RW, NULL);
    pthread_mutex_init(&W, NULL);

    // 创建线程
    pthread_t readers[5], writers[3];
    int reader_ids[5], writer_ids[3];

    for (int i = 0; i < 5; i++) {
        reader_ids[i] = i;
        pthread_create(&readers[i], NULL, reader, &reader_ids[i]);
    }

    for (int i = 0; i < 3; i++) {
        writer_ids[i] = i;
        pthread_create(&writers[i], NULL, writer, &writer_ids[i]);
    }

    // 等待线程结束
    for (int i = 0; i < 5; i++) {
        pthread_join(readers[i], NULL);
    }

    for (int i = 0; i < 3; i++) {
        pthread_join(writers[i], NULL);
    }

    // 销毁互斥锁
    pthread_mutex_destroy(&Mutex);
    pthread_mutex_destroy(&RW);
    pthread_mutex_destroy(&W);

    return 0;
}

RWEcmd.png