設計目標
- **用戶態**(非內核),適用高併發、短臨界區、低競爭場景。
- **跨平台**: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 問題、邏輯複雜 |
注意事項與最佳實務
- 勿在持有自旋鎖時調用阻塞 API(I/O、sleep、malloc 等),會導致死鎖或極大延遲。
- 臨界區必須極短:僅做簡單記憶體讀寫、計數器增減、指針交換。
- 高競爭時退化為互斥鎖:可嘗試自旋 N 次後 `pthread_mutex_lock`(混合鎖)。
- 內存序:GCC `__sync` 内建函數提供**全順序一致性**(sequentially consistent),足夠安全。C11 `` 可精確控制 `memory_order`。
- 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 實現。