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

C 語言實作跨平台讀寫鎖:從 pthread_rwlock 缺陷分析到原子操作完整實作

     編輯:關於C語言

C 語言實作跨平台讀寫鎖:從 pthread_rwlock 缺陷分析到原子操作完整實作

讀寫鎖適用於「大量讀、少量寫」場景。標準 pthread_rwlock_t 在高併發讀取下會導致寫入者飢餓(不滿足特性 3:寫等待時阻塞後續讀者)。本文分析缺陷,並使用 GCC/MSVC 內建原子操作,實作一個滿足三大特性、跨平台(Linux/Windows)的高效能讀寫鎖。

讀寫鎖三大特性

  1. 寫鎖定期間,所有嘗試加鎖(讀/寫)的執行緒阻塞。
  2. 讀鎖定期間,允許其他讀者進入;寫者阻塞。
  3. 關鍵: 有寫者等待時,後續新讀者應阻塞,避免寫者飢餓。

pthread_rwlock 實測缺陷

// 測試:10 個讀執行緒、2 個寫執行緒
// 結果:寫執行緒長時間無法取得鎖,甚至永久飢餓

pthread_rwlock 執行結果:寫入飢餓

自訂讀寫鎖資料結構

// 初始化只需歸零
struct rwlock {
    int rlock;  // 讀者計數
    int wlock;  // 寫者旗標 (0/1)
};
// 使用:struct rwlock lock = {0, 0};

核心邏輯(Linux/GCC 原子操作版)

// 讀鎖:優先檢查 wlock,成功遞增 rlock,若期間 wlock 變 1 則回滾
static void rwlock_rlock(struct rwlock *lock) {
    for (;;) {
        while (lock->wlock) __sync_synchronize();  // 自旋等待寫釋放
        __sync_add_and_fetch(&lock->rlock, 1);     // 原子 +1
        if (!lock->wlock) break;                   // 確認無寫者介入
        __sync_add_and_fetch(&lock->rlock, -1);    // 回滾
    }
}

// 寫鎖:原子 CAS 佔用 wlock,再等待所有讀者離開
static void rwlock_wlock(struct rwlock *lock) {
    while (__sync_lock_test_and_set(&lock->wlock, 1)) ;  // 自旋搶佔
    while (lock->rlock) __sync_synchronize();             // 等待讀者歸零
}

// 解鎖
static inline void rwlock_runlock(struct rwlock *lock) {
    __sync_add_and_fetch(&lock->rlock, -1);
}
static inline void rwlock_wunlock(struct rwlock *lock) {
    __sync_lock_release(&lock->wlock);
}

跨平台封裝:scatom.h

統一 GCC 與 MSVC (Windows) 原子操作介面:

#if defined(_MSC_VER)  // Windows / Visual Studio
    #include <Windows.h>
    #define ATOM_ADD_FETCH(v,a)  InterlockedAdd((LONG volatile*)&(v), (LONG)(a))
    #define ATOM_SET(v,a)        InterlockedExchange((LONG volatile*)&(v), (LONG)(a))
    #define ATOM_LOCK(v)         while(ATOM_SET(v,1)) Sleep(0)
    #define ATOM_UNLOCK(v)       ATOM_SET(v,0)
    #define ATOM_SYNC()          MemoryBarrier()
#elif defined(__GNUC__)  // GCC / Clang
    #define ATOM_ADD_FETCH(v,a)  __sync_add_and_fetch(&(v), (a))
    #define ATOM_SET(v,a)        __sync_lock_test_and_set(&(v), (a))
    #define ATOM_LOCK(v)         while(ATOM_SET(v,1)) usleep(2)
    #define ATOM_UNLOCK(v)       __sync_lock_release(&(v))
    #define ATOM_SYNC()          __sync_synchronize()
#endif

完整驗證程式(Linux/Windows 均通過)

// 測試同前:10 讀、2 寫
// 編譯:gcc -Wall -ggdb2 -I. -o test_atom_rwlock.out test_atom_rwlock.c -lpthread
// 執行:讀寫交錯正常,寫入不飢餓,所有執行緒正常退出

自訂讀寫鎖執行結果 Windows Visual Studio 執行結果 Linux GCC 執行結果

程式碼風格小結:指標宣告的美感

// 推薦風格:型別與 * 緊貼,變數名獨立
int * piyo;          // 宣告
int * heoo(int a, int * pi);  // 原型
int * heoo(int a, int b) { ... }  // 定義

避免 int* piyo, *hoge; 易誤讀;亦避免 int *piyo; 與定義處風格不一致。統一風格提升可維護性。

💡 工程感悟: 標準庫並非完美,理解其局限並能自行實作核心同步原語,是系統程式設計師的基本功。原子操作 + 記憶體屏障 是無鎖/鎖實作的基石。

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