多线程编程之读写锁

 

在《多线程编程之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)读锁和写锁一定要分开使用,否则达不到效果。

时间: 2024-11-08 11:43:52

多线程编程之读写锁的相关文章

Java多线程13:读写锁和两种同步方式的对比

读写锁ReentrantReadWriteLock概述 大型网站中很重要的一块内容就是数据的读写,ReentrantLock虽然具有 完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务),但是效率非常低.所以在JDK中提供了一种读写锁 ReentrantReadWriteLock,使用它可以加快运行效率. 读写锁表示两个锁,一个是读操作相关的锁,称为共享锁:另一个是写操作相关的锁,称为排他锁.我把这两个操作理解为三句话: 1.读和读之间不互斥,因为读操作不会有线程安全问题 2.

UNIX网络编程:读写锁

之前我们整理了互斥锁与条件变量问题它保证了共享资源的安全,但在多线程中我们也会经常对共享数据进行读.写操作.也就是说对某些资源的访问会 存在两种可能的情况,一种是访问必须是排查性的,就是独占的意思,这称作写操作:另一种情况就是访问方式可以是共享的,就是说可以有多个线程同时去访问某个资源,这种就称作读操作.这个问题模型是从对文件的读写操作中引申出来的. 读写锁比起mutex具有更高的适用性,具有更高的并行性,可以有多个线程同时占用读模式的读写锁,但是只能有一个线程占用写模式的读写锁,读写锁的三种状

多线程之使用读写锁ReentrantReadWriteLock实现缓存系统

简单地缓存系统:当有线程来取数据时,如果该数据存在我的内存中,我就返回数据:如果不存在我的缓存系统中,那么就去查数据库,返回数据的同时保存在我的缓存中. 其中涉及到读写问题:当多个线程执行读操作时(都加读锁),如果有数据返回:如果没有数据时,则让第一个读的线程,进行获取数据,然后进行写操作,这时需要第一个线程先释放掉读锁然后加写锁.第一个写完后,在家读锁,其他线程使用时判断,如果存在该数据,在直接过去读取不用加写锁. API上缓存例子如下: class CachedData { Object d

java多线程:ReentrantReadWriteLock读写锁使用

Lock比传统的线程模型synchronized更多的面向对象的方式.锁和生活似,应该是一个对象.两个线程运行的代码片段要实现同步相互排斥的效果.它们必须用同一个Lock对象. 读写锁:分为读锁和写锁.多个读锁不相互排斥,读锁与写锁相互排斥,这是由jvm自己控制的,你仅仅要上好对应的锁就可以.假设你的代码仅仅读数据,能够非常多人同一时候读.但不能同一时候写,那就上读锁.假设你的代码改动数据.仅仅能有一个人在写.且不能同一时候读取,那就上写锁.总之.读的时候上读锁,写的时候上写锁! Reentra

java多线程设计模式(3)读写锁模式

1 Read-Write Lock Pattern Read-Write Lock Pattern是一种将对于共享资源的访问与修改操作分离,称为读写分离.即访问是reader,修改是write,用单独的线程来处理.可以允许多个reader,但是不允许同时多个写入或者在读的过程中有写入. 由于对于实例状态的读取,并不会破坏状态的完整性且状态也不会修改,可以允许多个线程同时访问操作.但是若在写入的过程中,会更改实例的状态,此时就需要对于写入做保护,防止其他线程来进行读操作和写操作. 在多个线程共享一

【C/C++多线程编程之九】pthread读写锁

多线程编程之读写锁 Pthread是 POSIX threads 的简称,是POSIX的线程标准.  pthread读写锁把对共享资源的访问者分为读者和写者,读者只对共享资源进行读访问,写者只对共享资源进行写操作.在互斥机制,读者和写者都需要独立独占互斥量以独占共享资源,在读写锁机制下,允许同时有多个读者读访问共享资源,只有写者才需要独占资源.相比互斥机制,读写机制由于允许多个读者同时读访问共享资源,进一步提高了多线程的并发度.                   1.读写锁机制:      

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

Java多线程编程4--ReentrantReadWriteLock的使用(读写锁)

类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务.这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的.所以在JDK中提供了一种读写锁ReentrantReadWriteLock类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁ReentrantReadWriteLock来提升该方法的代码运行速度. 读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关

多线程并发编程之显示锁ReentrantLock和读写锁

在Java5.0之前,只有synchronized(内置锁)和volatile. Java5.0后引入了显示锁ReentrantLock. ReentrantLock概况 ReentrantLock是可重入的锁,它不同于内置锁, 它在每次使用都需要显示的加锁和解锁, 而且提供了更高级的特性:公平锁, 定时锁, 有条件锁, 可轮询锁, 可中断锁. 可以有效避免死锁的活跃性问题.ReentrantLock实现了 Lock接口: public interface Lock { //阻塞直到获得锁或者中