用 compare_exchange_weak 而不是 compare_exchange_strong 是因为其在 x86 上编译为单条 cmpxchg 指令、允许伪失败以降低总线争用,arm 上还可能省去内存屏障,配合 while 循环重试可提升自旋锁吞吐。

为什么用 compare_exchange_weak 而不是 compare_exchange_strong?
自旋锁要求极低延迟和高重试容忍度,compare_exchange_weak 在 x86 上通常编译为单条 cmpxchg 指令,而 compare_exchange_strong 可能附带循环重试逻辑;弱版本允许伪失败(spurious failure),这在自旋场景下反而是优势——它让 CPU 更早释放总线或避免缓存行争用,实际吞吐更高。ARM 等架构上弱版本还可能省去内存屏障开销。
关键点:伪失败不是 bug,是设计特性,必须在外层用 while 循环捕获并重试,不能当作错误处理。
自旋锁的最小可行实现长什么样?
一个正确、可工作的 std::atomic<bool></bool> 自旋锁只需几行,但每处都有讲究:
class spin_lock {
std::atomic<bool> locked_{false};
public:
void lock() {
bool expected = false;
while (!locked_.compare_exchange_weak(expected, true,
std::memory_order_acquire,
std::memory_order_relaxed)) {
expected = false; // 必须重置!weak 可能修改 expected
// 可选:_mm_pause() 或 std::this_thread::yield()
}
}
void unlock() {
locked_.store(false, std::memory_order_release);
}
};</bool>
注意三点:
-
expected必须在循环内重置为false——compare_exchange_weak成功时不变,失败时可能被设为当前值(即true),不重置会导致下次直接跳过尝试 - 成功路径用
std::memory_order_acquire,保证后续读写不被重排到锁获取之前 - 失败路径用
std::memory_order_relaxed,因为只是重试,无需同步语义
常见崩溃或死锁怎么来的?
绝大多数问题出在内存序或状态误判:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 忘记在
lock()循环里重置expected→ 无限循环(expected保持true,compare_exchange_weak永远不匹配) - 在
unlock()用std::memory_order_relaxed→ 其他线程可能永远看不到解锁,导致死锁 - 用
std::atomic<int></int>或std::atomic<void></void>模拟布尔状态 → 值非 0/1 时compare_exchange_weak行为不可预测,应始终用std::atomic<bool></bool>或明确的整型枚举 - 没加
_mm_pause()(x86)或__builtin_ia32_pause()→ 高竞争下持续占用流水线,显著拖慢其他核,甚至触发 CPU 过热降频
要不要加 yield 或 pause?
要,但得看场景:
低竞争(锁持有时间短、线程数少):可以省略,减少指令开销;
高竞争或长时间持有锁:必须加 _mm_pause()(x86/x64)或 std::this_thread::yield()(便携但开销大)。_mm_pause() 不会放弃时间片,只提示 CPU 当前是自旋等待,能降低功耗、缓解总线争用,实测在多核密集场景下可提升 10%–30% 吞吐。
别用 std::this_thread::sleep_for(1ns) —— 系统调用开销远超自旋成本,彻底违背自旋锁初衷。
真正麻烦的是锁嵌套和异常安全——spin_lock 本身不支持递归,一旦构造函数抛异常,unlock() 永远不会调用,资源就卡死了。生产环境建议优先用 std::mutex,除非你清楚自己在做什么、且 profiler 确认锁是瓶颈。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










