首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Win32 多執行緒同步實戰 (下):以 Mutex 實作讀寫鎖 (Reader-Writer Lock)

     編輯:關於C++

為什麼需要讀寫鎖?

上篇的互斥鎖 (Critical Section) 將所有存取序列化。但實際場景常為:多讀者可並發、讀寫/寫寫需互斥。例如快取、設定資料 — 讀取遠多於寫入。讀寫鎖能大幅提升並發度。

核心機制:WaitForMultipleObjects + Mutex 陣列

  • 建立 N 個 Mutex (N = 最大並發讀者數)。
  • 讀鎖: WaitForMultipleObjects(N, handles, FALSE, timeout) — 任一 Mutex 可用即成功 (獲取該 Mutex 所有權)。
  • 寫鎖: WaitForMultipleObjects(N, handles, TRUE, timeout) — 全部 Mutex 皆可用才成功 (獨佔所有)。
  • 釋放對應 ReleaseMutex。

原理:讀者佔用 1 個 Mutex,最多 N 人並發;寫者需佔用所有 N 個,與任何讀者/寫者互斥。

類別架構設計

1. 抽象基類 _RWLockBase — 定義介面、支援多型


class _RWLockBase {
    friend class RWLock;
protected:
    virtual DWORD ReadLock(int timeout) = 0;
    virtual void ReadUnlock(int handleIndex) = 0;
    virtual DWORD WriteLock(int timeout) = 0;
    virtual void WriteUnlock() = 0;
};

2. 模板實作類 RWLockBase<maxReadCount> — 編譯期決定 Mutex 數量


template  // 預設 3 讀者並發
class RWLockBase : public _RWLockBase {
    HANDLE handles[maxReadCount]; // 陣列置於堆疊,無動態配置
    // 實作四個虛函數...
    DWORD ReadLock(int timeout) {
        return ::WaitForMultipleObjects(maxReadCount, handles, FALSE, timeout);
    }
    void ReadUnlock(int idx) { ::ReleaseMutex(handles[idx]); }
    DWORD WriteLock(int timeout) {
        return ::WaitForMultipleObjects(maxReadCount, handles, TRUE, timeout);
    }
    void WriteUnlock() {
        for (int i=0; i

模板優勢: 客戶端僅需 class MyData : public RWLockBase<> { }; 一次指定參數,無須重複傳遞數量,且陣列置於堆疊避免動態配置開銷。

3. RAII 衛士類 RWLock — 管理鎖生命週期


class RWLock {
    _RWLockBase* _pObj;
    bool _readLock;
    bool _success;
    int _handleIndex; // 讀鎖時記錄取得的 Mutex 索引
public:
    RWLock(_RWLockBase* pObj, bool readLock=true, int timeout=3000)
        : _pObj(pObj), _readLock(readLock), _success(false), _handleIndex(-1) {
        if (!_pObj) return;
        if (_readLock) {
            DWORD ret = _pObj->ReadLock(timeout);
            if (ret < WAIT_ABANDONED) { _handleIndex = ret - WAIT_OBJECT_0; _success = true; }
        } else {
            DWORD ret = _pObj->WriteLock(timeout);
            if (ret < WAIT_ABANDONED) _success = true;
        }
    }
    ~RWLock() {
        if (!_pObj) return;
        if (_handleIndex > -1) _pObj->ReadUnlock(_handleIndex);
        else _pObj->WriteUnlock();
    }
    bool IsLockSuccess() const { return _success; }
};

使用方式


class MyData : public RWLockBase<> { /* 資料成員 */ };
MyData data;

// 讀取路徑
void ReadData() {
    RWLock lock(&data, true); // 讀鎖
    if (lock.IsLockSuccess()) { /* 安全讀取 */ }
}

// 寫入路徑
void WriteData() {
    RWLock lock(&data, false); // 寫鎖
    if (lock.IsLockSuccess()) { /* 安全寫入 */ }
}

效能權衡:模板+虛函數 vs 動態陣列

方案優點缺點
模板 + 虛函數 (本文)客戶端代碼極簡、陣列在堆疊、編譯期定參數每實例額外 4/8 字節 vptr、虛函數呼叫間接開銷
非模板 + 動態陣列無虛函數開銷需動態配置陣列 (heap)、構造時延遲、指標額外空間、客戶端需兩次指定大小

實測差異極微,推薦模板版本 以易用性優先。

實測:5 讀者 + 5 寫者,最大並發讀者 3


001 [15:07:28.484] read thread 0 started
002 [15:07:28.484] read thread 1 started
003 [15:07:28.484] read thread 2 started  // 3 讀者並發
004 [15:07:29.484] read thread 0 ended
005 [15:07:29.484] read thread 3 started  // 釋放一個槽位,第 4 讀者進入
006 [15:07:29.484] read thread 1 ended
007 [15:07:29.484] read thread 4 started  // 第 5 讀者進入
008 [15:07:29.484] read thread 2 ended
009 [15:07:30.484] read thread 3 ended
010 [15:07:30.484] read thread 4 ended    // 所有讀者結束
011 [15:07:30.484] write thread 0 started // 寫者取得獨佔
012 [15:07:31.078] write thread 0 ended
013 [15:07:31.078] write thread 1 started
014 [15:07:31.484] write thread 2 timeout // 等待超時 (3 秒)
015 [15:07:31.484] write thread 3 timeout
016 [15:07:31.484] write thread 4 timeout
017 [15:07:31.687] write thread 1 ended

完美展示:讀讀並發 (3)、讀寫互斥 (讀完才寫)、寫寫互斥 (序列化)、超時機制。

總結

結合 WaitForMultipleObjects、Mutex 陣列、模板參數化、RAII 封裝,實作出高效能、易用、異常安全的讀寫鎖。此模式廣泛適用於高讀低寫的共享資料結構 (快取、索引、設定表等)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved