线程上下文切换成本高吗?
更新时间:2026-09-18。问题驱动短答页。排查「切换次数过高」见 上下文切换过高怎么排查,原理见 上下文切换的开销。
一句话回答
单次切换本身通常是亚微秒到几微秒,看似不大,但它的真正代价是间接的:切换要保存/恢复寄存器和栈指针、切地址空间(进程切换还要刷 TLB)、使 CPU 缓存(L1/L2)和分支预测对新线程「变冷」,以及调度器本身的开销。线程数远超核数、频繁在就绪/阻塞间跳动时,这些成本会累积到吃掉可观 CPU。
成本由哪几部分组成
- 直接开销:保存/恢复上下文(寄存器、程序计数器、栈)、内核调度选择下一个线程。
- 地址切换开销:进程间切换切页表基址、刷 TLB;同进程的线程切换共享地址空间,TLB 影响小,所以线程切换通常比进程切换便宜。
- 缓存冷启动(往往最大):新线程的数据不在热缓存里,初期大量 cache miss,访存延迟陡增。
- 频率放大:单次便宜,但每秒几十万~上百万次 cs 时,占比显著。
怎么判断是不是问题
bash
vmstat 1 # 看 cs 列(每秒上下文切换次数)和 r(就绪队列)、us/sy
pidstat -w 1 # 看每个进程的 cswch/s(自愿,多为等 IO/锁)与 nvcswch/s(非自愿,被抢占)cswch/s高:线程大量主动阻塞——等锁、等 IO、等下游,多为锁竞争/IO 等待信号。nvcswch/s高:CPU 时间片用完被抢占,常是运行线程数远超核数在抢 CPU。
怎么降低
- 别盲目加线程:CPU 密集任务线程数贴近核数,线程过多只增切换,配置依据见线程池七大参数。
- 减少锁竞争:缩小临界区、分段/无锁结构、减少 futex 唤醒,配合 strace -c 看 futex 占比。
- IO 密集用异步/协程:协程在用户态调度,切换不进内核、代价更低,见异步和阻塞 IO 的区别。
- 降低唤醒风暴:批量处理、合并不必要的 notify,避免惊群。
- 绑核/亲和性:减少跨核迁移导致的缓存失效。
总结
线程上下文切换单次仅亚微秒到几微秒,直接保存恢复寄存器和调度的开销不大,真正贵的是切地址空间刷 TLB(进程切换更甚)、以及缓存与分支预测变冷造成的大量 cache miss;是否成为瓶颈要看 vmstat 的 cs 和 pidstat -w 的自愿(等锁/IO)与非自愿(线程过多被抢占)切换,应对方向是 CPU 密集任务线程数贴近核数、降低锁竞争与唤醒风暴、IO 密集改用协程异步并按需绑核,而不是靠无脑增加线程数来提吞吐。