进程、线程、协程的资源消耗简述
Contents
一、线程切换 vs 进程切换
地址空间与页表
进程拥有独立的虚拟地址空间和页表,切换进程时需更新页表并刷新 TLB(地址转换缓存),导致内存访问速度下降。而线程共享进程的地址空间和页表,切换时无需此操作,TLB 缓存保持有效。上下文保存的内容
• 进程切换:需保存完整的上下文,包括寄存器、程序计数器、栈指针、内存映射、文件描述符等。
• 线程切换:仅需保存线程私有的寄存器、栈和程序计数器,共享资源(如代码段、文件)无需处理。缓存利用率
进程切换会导致 CPU 缓存(如 L1/L2/L3)失效,需重新加载数据,降低性能。线程切换时,缓存因共享地址空间仍有效,减少了数据重载的开销。资源分配
进程是资源分配的基本单位(如内存、文件),切换时需重新分配资源;线程共享进程资源,切换仅涉及执行流调度。
二、协程切换 vs 线程切换
协程的切换开销更小,原因在于其 用户态调度 和 轻量级设计:
用户态调度
协程切换完全由用户态代码控制,无需陷入内核态,避免了 用户态-内核态切换 的开销。而线程切换需操作系统介入,涉及模式切换和内核调度。上下文信息更少
协程只需保存少量寄存器(如 PC、SP)和栈指针,且栈空间通常仅需 KB 级别(线程栈为 MB 级别)。例如,Go 协程的初始栈仅 2KB,而 Java 线程默认为 1MB。非阻塞与协作式调度
协程通过主动让出(如yield
或await
)实现协作式调度,减少抢占式调度的竞争和锁需求。线程通常依赖操作系统的抢占式调度,可能因频繁切换导致性能损耗。内存与并发效率
单线程可运行数万协程(如 Go 的 Goroutine),而同等数量线程会因内存和调度开销过大而崩溃。协程的轻量级特性尤其适合高并发 I/O 密集型任务。
维度 | 进程切换 | 线程切换 | 协程切换 |
---|---|---|---|
地址空间 | 切换(独立) | 不切换(共享) | 不切换(共享) |
上下文大小 | 大(含全部资源) | 较小(仅寄存器) | 极小(仅关键寄存器) |
调度模式 | 内核抢占式 | 内核抢占式 | 用户协作式 |
内存开销 | 高(独立资源) | 中(共享资源) | 极低(KB 级栈) |
线程通过共享资源减少开销,协程通过用户态轻量级调度进一步优化,两者均通过减少内核参与和资源复用来提升性能>。