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

跨平台用戶態自旋鎖實現:Windows (Interlocked) + Linux (GCC Built-in) 單頭文件庫

     編輯:關於C

設計目標

  • **用戶態**(非內核),適用高併發、短臨界區、低競爭場景。
  • **跨平台**:Windows (MSVC) + Linux (GCC ≥ 4.1.2)。
  • **C 接口**:無 C++ 依賴,可直接用於 C/C++/Rust/Go CGO 等項目。
  • **單頭文件**:`spinlock.h` 直接包含使用。

接口定義


#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    volatile long flag_;      // 鎖標誌(0=空閒, 1=持有)
    volatile long* spin_;     // 指向實際自旋變量(支援外部共享鎖變量)
} spin_lock_t;

void spin_init(spin_lock_t* lock, long* external_flag); // external_flag 為 NULL 時使用內部 flag_
void spin_lock(spin_lock_t* lock);                      // 阻塞獲取
int  spin_trylock(spin_lock_t* lock);                   // 非阻塞嘗試,成功返回 1
void spin_unlock(spin_lock_t* lock);                    // 釋放
int  spin_is_lock(const spin_lock_t* lock);             // 查詢當前鎖狀態(1=持有)

#ifdef __cplusplus
}
#endif

核心實現:原子操作抽象層

操作Windows (MSVC)Linux (GCC Built-in)語義
原子交換InterlockedExchange(ptr, val)__sync_lock_test_and_set(ptr, val)`*ptr = val` 返回舊值
原子或並獲取舊值InterlockedOr(ptr, val) (需 Win8+)__sync_fetch_and_or(ptr, val)`old = *ptr; *ptr |= val; return old;`
原子與並獲取新值InterlockedAnd(ptr, val) (需 Win8+)__sync_and_and_fetch(ptr, val)`*ptr &= val; return *ptr;`
原子加並獲取新值InterlockedAdd(ptr, val)__sync_add_and_fetch(ptr, val)`*ptr += val; return *ptr;`
原子比較交換InterlockedCompareExchange(ptr, new, old)__sync_val_compare_and_swap(ptr, old, new)CAS
編譯器屏障_ReadWriteBarrier()__sync_synchronize()防止指令重排

完整實現代碼 (spinlock.h)


#ifndef SPINLOCK_H
#define SPINLOCK_H

#include 

#ifdef _MSC_VER
    #include 
    #define SPIN_PAUSE()        Sleep(1)          // 用戶態無 PAUSE 指令,Sleep(1) 釋放時間片
    #define ATOMIC_XCHG(ptr,val) InterlockedExchange((volatile long*)(ptr), (val))
    #define ATOMIC_FETCH_OR(ptr,val) InterlockedOr((volatile long*)(ptr), (val))
    #define ATOMIC_AND_FETCH(ptr,val) InterlockedAnd((volatile long*)(ptr), (val))
    #define ATOMIC_ADD_FETCH(ptr,val) InterlockedAdd((volatile long*)(ptr), (val))
#elif defined(__GNUC__)
    #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)
        #error "GCC >= 4.1.2 required for __sync builtins"
    #endif
    #include 
    #define SPIN_PAUSE()        sched_yield()     // 釋放 CPU 時間片
    #define ATOMIC_XCHG(ptr,val) __sync_lock_test_and_set((volatile long*)(ptr), (val))
    #define ATOMIC_FETCH_OR(ptr,val) __sync_fetch_and_or((volatile long*)(ptr), (val))
    #define ATOMIC_AND_FETCH(ptr,val) __sync_and_and_fetch((volatile long*)(ptr), (val))
    #define ATOMIC_ADD_FETCH(ptr,val) __sync_add_and_fetch((volatile long*)(ptr), (val))
#else
    #error "Unsupported platform: only Windows (MSVC) and Linux (GCC) supported"
#endif

typedef struct {
    volatile long flag_;
    volatile long* spin_;
} spin_lock_t;

static inline void spin_init(spin_lock_t* lock, long* external_flag) {
    lock->flag_ = 0;
    lock->spin_ = external_flag ? external_flag : &lock->flag_;
}

static inline void spin_lock(spin_lock_t* lock) {
    while (ATOMIC_XCHG(lock->spin_, 1)) { // 交換為 1,若舊值非 0 則已被持有
        SPIN_PAUSE();
    }
}

static inline int spin_trylock(spin_lock_t* lock) {
    return ATOMIC_XCHG(lock->spin_, 1) == 0; // 舊值為 0 表示獲取成功
}

static inline void spin_unlock(spin_lock_t* lock) {
    ATOMIC_AND_FETCH(lock->spin_, 0); // 原子清零
}

static inline int spin_is_lock(const spin_lock_t* lock) {
    return ATOMIC_ADD_FETCH(lock->spin_, 0); // 原子加 0 讀取當前值
}

#endif // SPINLOCK_H

使用範例


// 共享資源
int counter = 0;
spin_lock_t lock;

void* worker(void* arg) {
    for (int i = 0; i < 100000; ++i) {
        spin_lock(&lock);
        ++counter;
        spin_unlock(&lock);
    }
    return NULL;
}

int main() {
    spin_init(&lock, NULL); // 使用內部 flag_
    pthread_t t1, t2;
    pthread_create(&t1, NULL, worker, NULL);
    pthread_create(&t2, NULL, worker, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Counter: %d (expected 200000)\n", counter);
    return 0;
}

進階用法:外部共享鎖變量


// 多個 spin_lock_t 共享同一個鎖變量(如陣列條帶鎖)
long shared_flags[16] = {0};
spin_lock_t locks[16];
for (int i = 0; i < 16; ++i) spin_init(&locks[i], &shared_flags[i]);

// 現在 locks[i] 操作共享 shared_flags[i],可用於哈希表分段鎖

效能與適用場景對比

鎖類型適用場景優點缺點
自旋鎖 (本實現)極短臨界區(< 1μs)、高併發、低競爭、無阻塞系統調用無上下文切換開銷、延遲極低長持有浪費 CPU、優先級反轉風險
互斥鎖 (pthread_mutex / CRITICAL_SECTION)通用、長臨界區、可能阻塞公平、可阻塞、OS 優化系統調用開銷大(~微秒)
無鎖 (CAS 迴圈)極簡單操作(計數器、指針更新)最高吞吐ABA 問題、邏輯複雜

注意事項與最佳實務

  1. 勿在持有自旋鎖時調用阻塞 API(I/O、sleep、malloc 等),會導致死鎖或極大延遲。
  2. 臨界區必須極短:僅做簡單記憶體讀寫、計數器增減、指針交換。
  3. 高競爭時退化為互斥鎖:可嘗試自旋 N 次後 `pthread_mutex_lock`(混合鎖)。
  4. 內存序:GCC `__sync` 内建函數提供**全順序一致性**(sequentially consistent),足夠安全。C11 `` 可精確控制 `memory_order`。
  5. Windows XP/Server 2003:`InterlockedOr/And` 不支援,若需相容請用 `InterlockedCompareExchange` 迴圈實現。

C++11 現代化封裝(可選)


#include 
class spinlock {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
    void lock()   { while (flag.test_and_set(std::memory_order_acquire)); }
    bool try_lock() { return !flag.test_and_set(std::memory_order_acquire); }
    void unlock() { flag.clear(std::memory_order_release); }
};
// 用法:std::lock_guard g(lock);

C++ 項目優先使用標準庫 `std::atomic_flag`,僅在 C 項目、跨語言邊界、或需精確控制平台原子操作時使用本 C 實現。

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