news 2026/5/1 11:27:50

C++自旋锁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++自旋锁

一、 什么是自旋锁?

核心定义:

自旋锁是一种非阻塞锁。当线程尝试获取锁失败时,它不会挂起(阻塞/让出 CPU),而是会在一个死循环中持续检查(忙等待 / Busy-Waiting)锁是否被释放。

直观隐喻

  • 互斥锁 (Mutex):你去洗手间,发现门锁了。你回到座位上睡觉。等里面的人出来了,管理员把你叫醒,你再去上。
    • 开销:睡觉(切换上下文)和被叫醒(调度)很累。
  • 自旋锁 (Spinlock):你去洗手间,发现门锁了。你站在门口,每隔 0.1 秒就敲门问:“好了没?好了没?”,直到里面的人出来。
    • 开销:你一直站着(占用 CPU),哪里也去不了。但是一旦门开了,你零延迟冲进去。

二、 为什么需要自旋锁?(底层视角)

您可能会问:“让线程空转浪费 CPU,这不是很傻吗?”

要理解它的价值,必须看**上下文切换(Context Switch)**的成本。

  1. Mutex 的成本
    • std::mutex拿不到锁时,线程会陷入内核态(Kernel Mode)。
    • OS 需要保存当前线程的寄存器、栈指针,刷新 TLB(页表缓存),然后调度另一个线程。
    • 这个过程大约需要3 ~ 10 微秒(在现代 CPU 上)。
  1. Spinlock 的优势
    • 如果您的临界区代码执行时间极短(比如只是做一个pNext = node;的链表操作),耗时可能只有0.01 微秒
    • 为了等待 0.01 微秒的任务,去花费 5 微秒切换线程,是亏本生意
    • 自旋锁全程在**用户态(User Mode)**运行,完全没有系统调用开销。

结论:自旋锁适用于**“锁持有时间极短”**的场景。


三、 C++ 中的自旋锁实现

C++ 标准库并没有直接提供std::spinlock(C++20 只有std::atomic_flag),我们需要利用原子操作自己实现。

1. 最基础的实现:std::atomic_flag

这是 C++ 中唯一保证**无锁(Lock-Free)**的数据类型。

#include <atomic> #include <thread> #include <vector> #include <iostream> class SpinLock { private: // atomic_flag 只有两个状态:set (true) 和 clear (false) // ATOMIC_FLAG_INIT 初始化为 false std::atomic_flag flag = ATOMIC_FLAG_INIT; public: void lock() { // test_and_set(): // 1. 读取当前值 // 2. 将值设为 true // 3. 返回旧值 // 这是一个原子操作 (RMW: Read-Modify-Write) // 如果返回 true,说明之前已经是 true (被别人锁了),则一直循环 (自旋) // memory_order_acquire: 保证获得锁之后的读写操作不会重排到加锁之前 while (flag.test_and_set(std::memory_order_acquire)) { // 这里是自旋区 (Spinning) // 可以在这里加 "CPU pause" 指令优化(后面会讲) } } void unlock() { // 清除标志,设为 false // memory_order_release: 保证解锁之前的读写操作全部完成 flag.clear(std::memory_order_release); } }; // 使用示例(配合 lock_guard 满足 RAII) SpinLock sl; void worker() { // std::lock_guard 需要类满足 BasicLockable (有 lock/unlock 方法) std::lock_guard<SpinLock> guard(sl); // 临界区... }
2. 通用实现:std::atomic<bool>

功能类似,但atomic<bool>可以提供更多 API(比如load查看状态),只是在极老的硬件上可能不是 Lock-Free 的(虽然现在几乎都是)。

C++

class SpinLockBool { std::atomic<bool> locked{false}; public: void lock() { bool expected = false; // CAS (Compare And Swap) // 尝试把 locked 从 false 改成 true // 如果 locked 是 true (被锁),compare_exchange_weak 返回 false,继续循环 while (!locked.compare_exchange_weak(expected, true, std::memory_order_acquire)) { expected = false; // CAS 失败后 expected 会被改成当前值(true),重置为 false 再次尝试 } } void unlock() { locked.store(false, std::memory_order_release); } };

四、 致命陷阱与性能优化(C++ 高阶)

在实现高性能组件(如内存池)时,直接用while(flag.test_and_set())会带来严重的性能问题。

1. 缓存一致性风暴 (Cache Coherence Storm / Bus Contention)
  • 现象:多个线程在一个原子变量上疯狂CAS(写操作)。
  • 原理:根据 CPU 的 MESI 协议,当一个核修改原子变量时,必须让其他核的 Cache Line 失效。如果 10 个线程同时自旋,锁变量所在的 Cache Line 会在 CPU 核心之间疯狂“跳来跳去”,导致总线流量爆炸,甚至拖慢其他不相关线程的速度。
  • 解决Test-Test-and-Set (TTAS)模式。
    • 先用load(读) 检查是否被释放(读操作不独占 Cache Line)。
    • 只有读到false时,才尝试CAS(写)。
2. CPU 流水线空转
  • 现象while循环是一个极紧密的指令序列,CPU 流水线会全速运行,产生大量热量并消耗电力。
  • 解决:CPU Pause 指令。

在 x86 架构下,使用 _mm_pause() 指令(SSE2 扩展)。

    1. 它告诉 CPU “我在自旋”,让 CPU 稍微降低流水线派发速度,节能降温。
    2. 它可以避免退出循环时的内存顺序冲突惩罚。

优化后的 C++ 代码:

#include <atomic> #include <immintrin.h> // for _mm_pause class OptimizedSpinLock { std::atomic_flag flag = ATOMIC_FLAG_INIT; public: void lock() { while (flag.test_and_set(std::memory_order_acquire)) { // 在自旋期间... while (flag.test(std::memory_order_relaxed)) { // 先只读 (Test) // 告诉 CPU 稍微休息一下,不要全速空转 #if defined(__x86_64__) || defined(_M_X64) _mm_pause(); #endif // 如果是 ARM 架构,可以用 __yield() 或 asm("yield") } } } void unlock() { flag.clear(std::memory_order_release); } };
3. 优先级反转与死锁
  • 场景:如果一个低优先级线程拿到了自旋锁,但因为被 OS 调度走了(时间片到了),一个高优先级线程被调度进来,尝试获取同一个自旋锁。
  • 结果:高优先级线程因为是自旋(忙等),它不让出 CPU,导致低优先级线程永远得不到 CPU 来执行解锁操作。于是死锁
  • 对策:在自旋锁中,如果自旋超过一定次数,必须使用std::this_thread::yield()主动让出时间片。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 10:02:13

RAG LLM BM25和BGE进行检索的先后顺序:BM25→BGE

RAG LLM BM25和BGE进行检索的先后顺序:BM25→BGE BM25和BGE是两种完全不同的检索技术,它们在信息检索流程中通常结合使用而非互斥选择。 核心区别对比 特性 BM25 BGE (BAAI General Embedding) 类型 传统统计模型 (稀疏检索) 深度学习模型 (稠密检索) 原理 基于词频、逆文…

作者头像 李华
网站建设 2026/5/1 10:42:23

基于springboot的中小学生英语学习阅读系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的设计程序开发&#xff0c;开发过上千套设计程序&#xff0c;没有什么华丽的语言&#xff0c;只有实…

作者头像 李华
网站建设 2026/5/1 10:29:35

【限时关注】金融行业加密标准解读:PHP如何合规实现非对称加密

第一章&#xff1a;金融支付中非对称加密的合规背景在金融支付系统中&#xff0c;数据安全与用户隐私保护是监管机构关注的核心议题。随着《支付卡行业数据安全标准》&#xff08;PCI DSS&#xff09;、《通用数据保护条例》&#xff08;GDPR&#xff09;以及中国《网络安全法》…

作者头像 李华