Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 讀寫鎖實現原理與核心程式碼詳解

編輯:Linux 教學

前言

讀寫鎖是並發編程中「讀者共享、寫者獨佔」的經典同步原語。Linux 核心(pthread)以 mutex + 兩個條件變數 實現,並採寫者優先策略防止寫者餓死。本文拆解 pthread_rwlock_t 結構與三大核心函數 rdlock、wrlock、unlock 的邏輯細節。

一、資料結構定義

typedef struct {
    pthread_mutex_t   rw_mutex;        // 保護整個結構體
    pthread_cond_t    rw_condreaders;  // 通知等待讀者
    pthread_cond_t    rw_condwriters;  // 通知等待寫者
    int               rw_waitreaders;  // 等待讀鎖線程數
    int               rw_waitwriters;  // 等待寫鎖線程數
    int               rw_refcount;     // 鎖狀態:-1=寫鎖,0=空閒,>0=讀鎖計數
} pthread_rwlock_t;

二、核心邏輯逐行解析

1. 申請讀鎖 pthread_rwlock_rdlock()

int pthread_rwlock_rdlock(pthread_rwlock_t *rw) {
    int result;
    pthread_mutex_lock(&rw->rw_mutex);

    // 寫者優先:有寫鎖被持有 OR 有寫者在等待 → 讀者必須等待
    while (rw->rw_refcount < 0 || rw->rw_waitwriters > 0) {
        rw->rw_waitreaders++;
        result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);
        rw->rw_waitreaders--;
        if (result != 0) break;
    }

    if (result == 0) rw->rw_refcount++;  // 成功獲取,讀者計數+1
    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

關鍵點:rw_refcount < 0 表示寫鎖被持有;rw_waitwriters > 0 實現寫者優先——新來的讀者不得插隊,必須等所有等待寫者先獲得鎖並釋放。

2. 申請寫鎖 pthread_rwlock_wrlock()

int pthread_rwlock_wrlock(pthread_rwlock_t *rw) {
    int result;
    pthread_mutex_lock(&rw->rw_mutex);

    // 僅檢查鎖是否空閒(refcount == 0),不在乎等待的讀者
    while (rw->rw_refcount != 0) {
        rw->rw_waitwriters++;
        result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);
        rw->rw_waitwriters--;
        if (result != 0) break;
    }

    if (result == 0) rw->rw_refcount = -1;  // 標記為寫鎖
    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

關鍵點:寫者只關心「鎖是否完全空閒」,不理會等待中的讀者。配合讀端的 rw_waitwriters > 0 判斷,形成寫者優先語意。

3. 釋放鎖 pthread_rwlock_unlock()

int pthread_rwlock_unlock(pthread_rwlock_t *rw) {
    int result = 0;
    pthread_mutex_lock(&rw->rw_mutex);

    if (rw->rw_refcount > 0)       rw->rw_refcount--;   // 讀鎖遞減
    else if (rw->rw_refcount == -1) rw->rw_refcount = 0; // 寫鎖歸零
    else printf("invalid refcount=%d\n", rw->rw_refcount);

    // 喚醒策略:寫者優先
    if (rw->rw_waitwriters > 0) {
        if (rw->rw_refcount == 0)               // 僅在鎖完全空閒時喚醒寫者
            result = pthread_cond_signal(&rw->rw_condwriters);
    } else if (rw->rw_waitreaders > 0) {
        result = pthread_cond_signal(&rw->rw_condreaders); // 無寫者等待才喚醒讀者
    }

    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

三、為何不能寫成 if (rw->rw_waitwriters > 0 && rw->rw_refcount == 0)?

若這樣寫,當「有寫者等待」且「仍有讀者持有鎖(refcount > 0)」時,條件為假,會落入 else if 喚醒讀者 → 新讀者獲取鎖 → 寫者持續被餓死。
正確邏輯:只要有寫者等待,釋放鎖時絕不喚醒讀者,只在鎖完全空閒(refcount==0)時喚醒一個寫者;否則什麼都不做,等最後一個讀者釋放鎖時再喚醒寫者。

四、總結:讀寫鎖四大不變量

  1. 寫鎖持有時,任何鎖都不可再加。
  2. 讀鎖持有時,可加讀鎖,不可加寫鎖。
  3. 有寫者等待時,新讀者不可獲取讀鎖(寫者優先)。
  4. 解鎖時若有寫者等待,優先喚醒寫者(不喚醒讀者)。

這四條不變量由 rw_refcount 正負號、rw_waitwriters 計數、條件變數精密協作保證。理解此實現,有助於排查死鎖、餓死、優先級反轉等並發疑難雜症。

Copyright © Windows教程網 All Rights Reserved