C++ WonderTrader源码分析之自旋锁实现
一、介绍
在WonderTrader的文件SpinMutex.hpp定义了跨平台的自旋锁的实现。
二、实现原理
1、类 SpinMutex:自旋锁实现
SpinMutex 是一个轻量级的自旋锁(Spinlock)实现,用于多线程之间保护临界区资源。自旋锁通过不断尝试获取锁而不让出 CPU 控制权,在临界区非常短的场景下比 std::mutex 更高效
类定义与成员变量
class SpinMutex
{
private:std::atomic<bool> flag = { false };
含义:
- flag 是一个原子布尔变量,标志锁的状态:
- false:锁未被占用。
- true:锁已被占用。
- 使用 std::atomic 保证该变量在线程之间的读写是原子的,不会产生数据竞争。
lock() 方法:尝试加锁(进入临界区)
void lock()
{for (;;){if (!flag.exchange(true, std::memory_order_acquire))break;while (flag.load(std::memory_order_relaxed)){
#ifdef _MSC_VER_mm_pause();
#else__builtin_ia32_pause();
#endif}}
}
(1)、尝试抢锁
if (!flag.exchange(true, std::memory_order_acquire))break;
含义:
- 尝试将 flag 从 false 变成 true。
- exchange 是一个原子操作,返回交换前的值。
- 如果原值是 false,说明锁空闲,抢锁成功,跳出循环。
- memory_order_acquire 的作用:
- 表示获取操作:确保当前线程在获取锁之后,看到的所有共享内存变动是“可见的”。
- 保证锁之后的内存操作不会重排到 exchange 之前。
(2)、锁被占用,进入自旋等待
while (flag.load(std::memory_order_relaxed))
{
#ifdef _MSC_VER_mm_pause();
#else__builtin_ia32_pause();
#endif
}
含义:
- 如果抢锁失败,说明 flag == true,就会进入这个 while,不断检测锁是否释放。
- 自旋期间只读取,不尝试修改。
- memory_order_relaxed:
- 只要求原子性,不需要内存可见性或顺序一致性。
- 适合用于自旋中不断读状态,无需同步其他内存操作。
- _mm_pause() / __builtin_ia32_pause():
- 都是CPU pause 指令,告诉处理器“我在忙等,不要发热也 不要让乱序执行干扰这个循环”。
- 优化超线程(HT)下的性能,降低能耗和缓存一致性压力。
- _mm_pause() 是 Intel/微软的,__builtin_ia32_pause() 是 GCC/Clang 提供的
这段代码的整体思路:
1 尝试交换(CAS):把 flag 从 false 设置为 true。
2 成功 → 抢到锁,退出。
3 失败 → 等着别人释放(即 flag 变为 false)。
4 不断读(load)+ pause,直到别人释放锁,继续尝试抢锁。
unlock() 方法:释放锁
void unlock()
{flag.store(false, std::memory_order_release);
}
含义:
- 将 flag 设为 false,表示锁释放,别人可以抢锁了。
- memory_order_release:
- 表示释放操作:保证当前线程在释放锁前对共享内存的所有写操作,对下一个加锁的线程是可见的。
- 保证写操作不会重排序到 store 之后。
整体流程
Thread A Thread B
--------- ----------
flag == false
↓
exchange(true) → 成功
flag = true(锁住)
执行临界区
↓
store(false)(解锁) load(flag) → falseexchange(true) → 成功进入临界区
2、类 SpinLock:RAII 自旋锁管理器
构造函数:
SpinLock(SpinMutex& mtx) :_mutex(mtx) { _mutex.lock(); }
接收一个 SpinMutex 引用,并立即加锁。
析构函数:
~SpinLock() { _mutex.unlock(); }
离开作用域时自动释放锁,避免忘记 unlock() 导致死锁。
拷贝构造 & 赋值删除:
SpinLock(const SpinLock&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
禁止复制和赋值,避免一个锁对象被多次释放,保证线程安全。
三、使用举例
#include "SpinMutex.hpp"
#include <vector>
#include <thread>SpinMutex spin_mtx;
int counter = 0;
void worker(int id, int loops)
{for (int i = 0; i < loops; ++i){SpinLock lock(spin_mtx); // 自动加锁++counter; // 临界区// 离开作用域时自动解锁}
}
int main() {int thread_count = 4;int loops = 100000;std::vector<std::thread> threads;threads.reserve(thread_count);for (int i = 0; i < thread_count; ++i)threads.emplace_back(worker, i, loops);for (auto& t : threads)t.join();std::cout << "Final counter = " << counter << std::endl;
}