操作系统 c 读者写者问题
操作系统 c 读者写者问题
问题描述:
如果你建立了一个数据库,同时可以通过多个线程被多个远程用户使用,其中一些用户只需要查看数据,而另一些用户需要修改数据。
显然:
- 你不能允许两个用户同时修改数据,
- 但你也不能允许一个用户在修改数据时、另一个用户来读取数据,因为这时后一个用户读取到的数据可能是没有被完全更新的数据。
因此我们需要一个特殊的管程,在没有写入数据的线程时,它允许多个线程同时读取数据;在有线程读取数据时,想要写入数据的线程就需要等待;在有线程写入数据时,所有其它线程均需要等待;在同时有读者和写者等待时,优先运行写者线程,使得读者获取最新数据。
代码封装
我们可以定义一个新的锁,叫做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);