一、线程切换 vs 进程切换

  1. 地址空间与页表
    进程拥有独立的虚拟地址空间和页表,切换进程时需更新页表并刷新 TLB(地址转换缓存),导致内存访问速度下降。而线程共享进程的地址空间和页表,切换时无需此操作,TLB 缓存保持有效。

  2. 上下文保存的内容
    进程切换:需保存完整的上下文,包括寄存器、程序计数器、栈指针、内存映射、文件描述符等。
    线程切换:仅需保存线程私有的寄存器、栈和程序计数器,共享资源(如代码段、文件)无需处理。

  3. 缓存利用率
    进程切换会导致 CPU 缓存(如 L1/L2/L3)失效,需重新加载数据,降低性能。线程切换时,缓存因共享地址空间仍有效,减少了数据重载的开销。

  4. 资源分配
    进程是资源分配的基本单位(如内存、文件),切换时需重新分配资源;线程共享进程资源,切换仅涉及执行流调度。

二、协程切换 vs 线程切换

协程的切换开销更小,原因在于其 用户态调度轻量级设计

  1. 用户态调度
    协程切换完全由用户态代码控制,无需陷入内核态,避免了 用户态-内核态切换 的开销。而线程切换需操作系统介入,涉及模式切换和内核调度。

  2. 上下文信息更少
    协程只需保存少量寄存器(如 PC、SP)和栈指针,且栈空间通常仅需 KB 级别(线程栈为 MB 级别)。例如,Go 协程的初始栈仅 2KB,而 Java 线程默认为 1MB。

  3. 非阻塞与协作式调度
    协程通过主动让出(如 yieldawait)实现协作式调度,减少抢占式调度的竞争和锁需求。线程通常依赖操作系统的抢占式调度,可能因频繁切换导致性能损耗。

  4. 内存与并发效率
    单线程可运行数万协程(如 Go 的 Goroutine),而同等数量线程会因内存和调度开销过大而崩溃。协程的轻量级特性尤其适合高并发 I/O 密集型任务。

维度进程切换线程切换协程切换
地址空间切换(独立)不切换(共享)不切换(共享)
上下文大小大(含全部资源)较小(仅寄存器)极小(仅关键寄存器)
调度模式内核抢占式内核抢占式用户协作式
内存开销高(独立资源)中(共享资源)极低(KB 级栈)

线程通过共享资源减少开销,协程通过用户态轻量级调度进一步优化,两者均通过减少内核参与和资源复用来提升性能>。