多线程编程之读写锁

Posted 拙园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程编程之读写锁相关的知识,希望对你有一定的参考价值。

 

在《多线程编程之Linux环境下的多线程(二)》一文中提到了Linux环境下的多线程同步机制之一的读写锁。本文再详细写一下读写锁的概念和原理。

一、什么是读写锁

  读写锁(也叫共享-独占锁)实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。

  如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须“自旋”在那里,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须“自旋”在那里,直到写者释放该读写锁。读写锁适合于对数据结构的读次数比写次数多很多的场合。

二、一种Linux环境下的实现方法

  下面利用pthread.h提供的mutex和condition来实现一个读写锁:

复制代码
#include <pthread.h>

struct rwlock {
    pthread_mutex_t lock;
    pthread_cond_t read, write;
    unsigned readers, writers, read_waiters, write_waiters;
};

void reader_lock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    if (self->writers || self->write_waiters) {
        self->read_waiters++;
        do pthread_cond_wait(&self->read, &self->lock);
        while (self->writers || self->write_waiters);
        self->read_waiters--;
    }
    self->readers++;
    pthread_mutex_unlock(&self->lock);
}

void reader_unlock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    self->readers--;
    if (self->write_waiters)
        pthread_cond_signal(&self->write);
    pthread_mutex_unlock(&self->lock);
}

void writer_lock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    if (self->readers || self->writers) {
        self->write_waiters++;
        do pthread_cond_wait(&self->write, &self->lock);
        while (self->readers || self->writers);
        self->write_waiters--;
    }
    self->writers = 1;
    pthread_mutex_unlock(&self->lock);
}

void writer_unlock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    self->writers = 0;
    if (self->write_waiters)
        pthread_cond_signal(&self->write);
    else if (self->read_waiters)
        pthread_cond_broadcast(&self->read);
    pthread_mutex_unlock(&self->lock);
}

void rwlock_init(struct rwlock *self) {
    self->readers = self->writers = self->read_waiters = self->write_waiters = 0;
    pthread_mutex_init(&self->lock, NULL);
    pthread_cond_init(&self->read, NULL);
    pthread_cond_init(&self->write, NULL);
}
复制代码

   这种实现方式可以在有写锁存在的情况下不增加读锁数量,而是累加读等待数目。

三、一种Windows环境下的实现方法

  在Windows环境下实现方式也是差不多的,参考如下实例:

复制代码
typedef struct _RWLock    
{
    int count;
    int state;
    HANDLE hRead;
    HANDLE hWrite;
} RWLock;  
typedef enum    /* 枚举读写状态 */
{
    STATE_EMPTY = 0,
    STATE_READ,
    STATE_WRITE
};
RWLock* create_read_write_lock(HANDLE hRead, HANDLE hWrite)
{
    RWLock* pRwLock = NULL;
    assert(NULL != hRead && NULL != hWrite);
    pRwLock = (RWLock*)malloc(sizeof(RWLock));
  
    pRwLock->hRead = hRead;
    pRwLock->hWrite = hWrite;
    pRwLock->count = 0;
    pRwLock->state = STATE_EMPTY;
    return pRwLock;
}
void read_lock(RWLock* pRwLock)
{
    assert(NULL != pRwLock);
    WaitForSingleObject(pRwLock->hRead, INFINITE);
    pRwLock->count ++;
    if(1 == pRwLock->count){
        WaitForSingleObject(pRwLock->hWrite, INFINITE);
        pRwLock->state = STATE_READ;
    }
    ReleaseMutex(pRwLock->hRead);
}
void write_lock(RWLock* pRwLock)
{
    assert(NULL != pRwLock);
    WaitForSingleObject(pRwLock->hWrite, INFINITE);
    pRwLock->state = STATE_WRITE;
}
void read_write_unlock(RWLock* pRwLock)
{
    assert(NULL != pRwLock);
    if(STATE_READ == pRwLock->state){
        WaitForSingleObject(pRwLock->hRead, INFINITE);
        pRwLock->count --;
        if(0 == pRwLock->count){
            pRwLock->state = STATE_EMPTY;
            ReleaseMutex(pRwLock->hWrite);
        }
        ReleaseMutex(pRwLock->hRead);
    }else{
        pRwLock->state = STATE_EMPTY;
        ReleaseMutex(pRwLock->hWrite);
    }
    return;
}
复制代码

  Windows环境下的实现方式要更加简单一些,不过这种方式的写操作一旦有读操作获取了锁,就只能等待所有读操作执行完了才行。如果一直都有读操作,那么写操作将会一直等待下去。

四、读写锁使用的经验总结

(1)读写锁的优势只有在多读少写、代码段运行时间长这两个条件下才会效率达到最大化;
(2)任何公共数据的修改都必须在锁里面完成;
(3)读写锁有自己的应用场所,选择合适的应用环境十分重要;
(4)编写读写锁很容易出错,需要多加练习;
(5)读锁和写锁一定要分开使用,否则达不到效果。

以上是关于多线程编程之读写锁的主要内容,如果未能解决你的问题,请参考以下文章

多线程编程之顺序锁

多线程编程之synchronized和Lock

多线程之线程同步(互斥锁信号量条件变量和读写锁​)

多线程之线程同步(互斥锁信号量条件变量和读写锁​)

多线程之线程同步(互斥锁信号量条件变量和读写锁​)

JAVA 并发编程-读写锁之模拟缓存系统