卡码笔记-最强八股文
首页
计算机基础
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
简历专栏
代码随想录 (opens new window)
首页
计算机基础
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
简历专栏
代码随想录 (opens new window)
  • 本栏必读

    • 关于本专栏
    • C++学习路线
    • C++面试题系优化
  • 基础与语法

  • 面向对象

  • STL 与容器

  • 内存管理

  • C++11 与现代 C++

  • 智能指针

  • 并发与 I/O

    • 互斥锁与自旋锁
      • 简要回答
      • 详细回答
      • 知识拓展
    • 说一下select,poll和epoll

# 自旋锁与互斥锁的性能对比及适用场景

面试官问"自旋锁和互斥锁有什么区别,什么时候用哪个",大部分人能说出"自旋锁忙等待,互斥锁会睡眠",但追问"性能拐点在哪里""单核为什么不能用自旋锁""自旋锁会不会活锁"就答不上来了。这道题的核心是理解:选择哪种锁取决于临界区执行时间和上下文切换开销的比较。

# 简要回答

自旋锁在用户态死循环检测锁状态(忙等待),拿到锁的瞬间延迟极低,但等待期间 CPU 一直在空转。互斥锁通过系统调用让拿不到锁的线程睡眠,CPU 可以去干别的事,但唤醒线程需要用户态→内核态→用户态两次切换,延迟高。判断标准很简单:如果临界区的执行时间短于两次上下文切换的开销(通常 1-10μs),用自旋锁划算;否则用互斥锁。

# 详细回答

自旋锁怎么工作的

自旋锁的核心是一个原子变量(比如 std::atomic_flag)。加锁时用 test_and_set 原子操作尝试把标志从 0 改成 1,如果失败说明别人持有锁,就在一个 while 循环里不断重试。解锁时把标志清零。整个过程不涉及系统调用,不会让线程睡眠,所以没有上下文切换开销。代价是等待期间 CPU 核心被完全占用,什么有用的事都干不了。

互斥锁怎么工作的

互斥锁(std::mutex)加锁失败时,会通过系统调用(Linux 上是 futex)把当前线程放到等待队列里并让它睡眠。持有锁的线程解锁时,内核会唤醒等待队列里的一个线程。这个过程涉及用户态到内核态的切换(保存/恢复寄存器、切换页表等),开销大约 1-10μs。好处是睡眠期间 CPU 核心可以执行其他线程。

性能拐点在哪里

如果临界区只做一两次内存读写(几十纳秒),自旋锁的忙等待开销远小于互斥锁的两次上下文切换开销,自旋锁性能更好。如果临界区要做 IO 操作、复杂计算(几十微秒以上),自旋锁让等待线程白白烧 CPU,互斥锁让线程睡眠反而更高效。经验值:临界区执行时间超过 10-20μs 就该用互斥锁。

单核为什么不能用自旋锁

单核 CPU 上只有一个执行流。如果线程 A 持有锁,线程 B 开始自旋等待,但 B 占着唯一的 CPU 核心不放,A 根本没机会运行来释放锁——形成类似死锁的状态。只有在可抢占内核(时间片到了强制切换)或者 B 主动 yield() 让出 CPU 的情况下,单核才能勉强用自旋锁,但这时候自旋锁的优势已经没了。

自旋锁与互斥锁工作流程对比

# 知识拓展

Q:自旋锁会不会活锁?

会。多个线程同时自旋竞争同一把锁时,可能出现"惊群效应"——所有线程都在自旋但谁也拿不到锁。解决办法:引入随机退避(每次失败后随机等待一小段时间再重试)、使用票据锁(ticket lock)保证公平性、或者用 MCS 锁减少缓存行争用。

Q:如何确定自旋次数上限?

实际工程中常用"自适应自旋"策略:先自旋一定次数(比如上下文切换开销的 1.5-2 倍),如果还拿不到锁就退化成互斥锁让线程睡眠。Java 的 synchronized 和 Linux 内核的 mutex_lock 都用了这种混合策略。

Q:C++ 标准库有自旋锁吗?

没有专门的自旋锁类,但可以用 std::atomic_flag 的 test_and_set + clear 自己实现。C++20 新增了 std::atomic_flag::wait() 和 notify_one(),可以实现更高效的等待。实际项目中如果需要自旋锁,通常用平台相关的实现(如 Linux 的 pthread_spinlock_t)。

Q:读写锁和自旋锁/互斥锁是什么关系?

读写锁是更高层的抽象——多个读者可以同时持有锁,但写者独占。读写锁的底层实现可以基于自旋锁(读多写少+短临界区场景)或互斥锁(长临界区场景)。C++ 标准库的 std::shared_mutex 底层通常基于互斥锁实现。

Last Updated: 5/23/2026, 4:51:07 PM

← C++11中的智能指针线程安全性 说一下select,poll和epoll →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪