欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

操作系统 c 读者写者问题

程序员文章站 2022-07-04 23:34:32
...

操作系统 c 读者写者问题

问题描述:

如果你建立了一个数据库,同时可以通过多个线程被多个远程用户使用,其中一些用户只需要查看数据,而另一些用户需要修改数据。

显然:

  1. 你不能允许两个用户同时修改数据,
  2. 但你也不能允许一个用户在修改数据时、另一个用户来读取数据,因为这时后一个用户读取到的数据可能是没有被完全更新的数据。

​ 因此我们需要一个特殊的管程,在没有写入数据的线程时,它允许多个线程同时读取数据;在有线程读取数据时,想要写入数据的线程就需要等待;在有线程写入数据时,所有其它线程均需要等待;在同时有读者和写者等待时,优先运行写者线程,使得读者获取最新数据。

代码封装

我们可以定义一个新的锁,叫做rdwr_lock,每个用户进入这个系统时都需要先获得这个锁,然后才能执行读写操作。虽然我们叫它“锁”,但严格意义上来讲,它并不是完全互斥的,多个读者可以同时获得锁,我们需要实现的是写者之间的互斥和写者与读者之间的互斥,因此我们需要两个变量:读者数量与写者数量,以支持两种不同的获得锁的模式。同时,我们需要一个锁来保护rdwr_lock的内部数据。那么我们可以初步定义struct rdwr_lock如下:

struct rdwr_lock{
    pthread_mutex_t mutex;
    int reader_number;
    int writer_number;
};

按照我们之前提到的条件,当写者数量大于0时,读者和写者都需要等待,而当读者数量大于0时,写者也需要等待,因此我们需要两个条件变量来实现针对这两个条件的等待。因此我们需要更新struct rdwr_lock如下:

struct rdwr_lock{
    pthread_mutex_t mutex;
    pthread_cond_t can_read;
    pthread_cond_t can_write;
    int reader_number;
    int writer_number;
    int waiting_readers;
    int waiting_writers;
};

由于读者与写者获取锁的条件不同,我们现在定义如下的四个函数,分别给读者、写者用来获取、释放锁:

void acquire_read_lock(struct rdwr_lock *lock);
void unlock_read_lock(struct rdwr_lock *lock);
void acquire_write_lock(struct rdwr_lock *lock);
void unlock_write_lock(struct rdwr_lock *lock);

现在我们先来实现第一个函数void acquire_read_lock(struct rdwr_lock *lock);。像我们前面提到的那样,在有线程正在写入数据或有写者线程正在等待时,我们就需要等待,因此函数实现如下:

void acquire_read_lock(struct rdwr_lock *lock){
    pthread_mutex_lock(&lock->mutex);
    while (lock->writer_number + lock->waiting_writers > 0) {
        lock->waiting_readers += 1;
        pthread_cond_wait(&lock->can_read, lock);
        lock->waiting_readers -= 1;
    }
    lock->reader_number += 1;
    pthread_mutex_unlock(&lock->mutex);
}

鉴于读者的存在不会导致其它读者被阻塞,在一个读者离开系统时,它只需要查看系统内是否有等待的写者。因此我们可以实现void unlock_read_lock(struct rdwr_lock *lock);如下:

void unlock_read_lock(struct rdwr_lock *lock){
    pthread_mutex_lock(&lock->mutex);
    lock->reader_number -= 1;
    if (lock->waiting_writers>0) {
        pthread_cond_signal(&lock->can_write);
    }
    pthread_mutex_unlock(&lock->mutex);
}

类似的,我们可以实现写者进入和离开系统的函数:

void acquire_write_lock(struct rdwr_lock *lock){
    pthread_mutex_lock(&lock->mutex);
    while (lock->writer_number + lock->reader_number > 0) {
        lock->waiting_writers += 1;
        pthread_cond_wait(&lock->can_write, lock);
        lock->waiting_writers -= 1;
    }
    lock->writer_number += 1;
    pthread_mutex_unlock(&lock->mutex);
}

void unlock_write_lock(struct rdwr_lock *lock){
    pthread_mutex_lock(&lock->mutex);
    lock->write_number -= 1;
    if (lock->waiting_writers>0) {
        pthread_cond_signal(&lock->can_write);
    } else if (lock->waiting_readers>0) {
        pthread_cond_broadcast(&lock->can_read);
    }
    pthread_mutex_unlock(&lock->mutex);
}

需要注意的是,一个写者既能阻塞写者,也能阻塞读者,所以它在离开系统时必须检查系统中是否存在等待的写者和读者。如果有等待的写者,则它优先唤醒写者,否则它唤醒 所有等待的读者

以上来自计蒜客。。 大致上原理是可以理解的

读写锁介绍

     读写锁的类型是pthread_rwlock_t,如果这个类型的某个变量是静态分配的,那么可以通过PTHREAD_RWLOCK_INITIALIZER来初始化它。

            pthread_rwlock_rdlock获取一个读出锁,如果对应的读写锁已由某个写入者持有,那就阻塞调用线程。pthread_rwlock_wrlock获取一个写入锁,如果对应的读写锁已由另一个写入者持有,或者已由一个或多个读出者持有,那就阻塞调用线程。pthread_rwlock_unlock释放一个读出锁或写入锁。

	pthread_rwlock_rdlock(pthread_rwlock_t *rwpt);
	pthread_rwlock_wrlock(pthread_rwlock_t *rwpt);
	pthread_rwlock_unlock(pthread_rwlock_t *rwpt);