必须用 std::atomic_flag 实现自旋锁——它是唯一强制 lock-free 的原子类型,需用 ATOMIC_FLAG_INIT 初始化,配合 acquire/release 内存序、yield 或 pause 优化,并仅在临界区极短且低竞争时使用。

直接用 std::atomic_flag 实现自旋锁是最稳妥的选择——它被标准保证为 lock-free,且无构造/析构开销,比 std::atomic<bool></bool> 更底层、更轻量。
为什么必须用 std::atomic_flag 而不是 std::atomic<bool></bool>
std::atomic_flag 是 C++ 唯一被强制要求 lock-free 的原子类型;std::atomic<bool></bool> 在某些平台(如旧版 ARM 或嵌入式 GCC)可能退化为内部加互斥锁实现,那就不是真自旋锁了。实际测试中,flag.is_lock_free() 返回 true 才算过关。
- 初始化必须用
ATOMIC_FLAG_INIT(C++17 起可用std::atomic_flag{};默认初始化为 clear) - 不能用
= false或.store(false)初始化,否则行为未定义 -
test_and_set()和clear()是仅有的两个成员函数,语义清晰、不可误用
lock() 里不加 std::this_thread::yield() 会吃满 CPU
纯空循环(如 while(flag.test_and_set(...)) {})在单核或高负载多核下会导致线程持续抢占调度器时间片,不仅浪费算力,还可能饿死其他线程。这不是理论风险,实测中常见于容器密集型微服务。
- 在自旋循环体内加
std::this_thread::yield(),提示调度器“我暂时让出”,降低优先级但不挂起 - 更优做法是插入 CPU pause 指令:
_mm_pause()(x86/x64)或__builtin_ia32_pause(),减少功耗和总线争用 - 注意:不要用
std::this_thread::sleep_for(1ns)——这会触发系统调用,完全违背自旋锁初衷
memory_order_acquire 和 memory_order_release 不可省略
没有显式指定内存序时,test_and_set() 和 clear() 默认使用 memory_order_seq_cst,性能稍差;但若随意降级为 relaxed,会导致临界区内的读写被重排到锁外,引发数据竞争。
立即学习“C++免费学习笔记(深入)”;
-
lock()中的test_and_set(std::memory_order_acquire)确保后续所有读操作不会被重排到加锁前 -
unlock()中的clear(std::memory_order_release)确保之前所有写操作不会被重排到解锁后 - 二者配对构成 acquire-release 同步,在多核间建立 happens-before 关系
真正难的不是写出来,而是判断该不该用:如果临界区平均执行时间超过 1 微秒,或者锁竞争概率高于 5%,自旋锁大概率会拖垮整体吞吐——这时候宁可用 std::mutex,别硬扛。


















