1. 程式人生 > >多線程編程之讀寫鎖

多線程編程之讀寫鎖

|| 概念 release 線程編程 相關 修改 reader lin 實現

 

在《多線程編程之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)讀鎖和寫鎖一定要分開使用,否則達不到效果。

多線程編程之讀寫鎖