进程是什么,协程是什么
我们可以把计算机比作一个大工厂,从资源管理和执行效率两个维度来拆解。
先上核心结论:进程是“资源分配”的最小单位,线程是“CPU调度”的最小单位,而协程是“用户态”的轻量级执行单元。
1. 进程(Process):工厂里的“独立车间”
是什么:进程是操作系统进行资源分配(内存、文件句柄、网络端口)的基本单位。它是正在运行的程序的一个实例。
工厂类比:相当于一个独立的车间。每个车间都拥有自己完整的地盘(独立的内存空间)、物料(数据)和机器(资源)。
核心特点(隔离性):
独立性:进程间的内存是相互隔离的。车间 A 无法直接访问车间 B 的内存,如果 A 崩溃了,完全不影响 B(所以多进程安全性最高)。
重量级:创建和销毁一个进程,需要操作系统分配大量的系统资源(页表、PCB控制块等),开销很大。
切换成本高:CPU 从一个进程切换到另一个进程,需要保存当前全部运行状态,还要刷新缓存(TLB),速度较慢。
2. 协程(Coroutine / Goroutine):车间里的“流水线工人”
是什么:协程是比线程更轻量级的用户态执行单元。它完全由编程语言运行时(如 Go 的调度器)进行管理和调度,而不是由操作系统内核管理。
工厂类比:相当于车间里一个个灵活的兼职工人(针对 Go 的 Goroutine,则是“超轻量虚拟工人”)。他们没有固定的工位(栈空间极小,Go 中初始仅 2KB),可以随时被组长安排到某个机器上干活,干完立刻让出位置。
核心特点(轻量且高效):
用户态调度:创建、销毁、切换都在用户态完成,不陷入内核态,不涉及系统调用,速度极快(纳秒级)。
栈空间动态伸缩:内存占用极小,且能自动扩容,一台普通的 16GB 服务器轻松创建几十万甚至上百万个(有别于我们之前纠正的“10万”误区)。
非抢占式/协作式(通常):大多数协程需要主动让出执行权(Go 的 Goroutine 在阻塞 I/O 或调用
runtime.Gosched时会自动让出)。
关键区分:进程、线程、协程的终极对比
为了帮你彻底理清,这里加上线程(Thread)来做对比(线程是进程内的执行者,属于内核级调度):
| 维度 | 进程 | 线程 | 协程 |
|---|---|---|---|
| 管理方 | 操作系统内核 | 操作系统内核 | 用户态(语言运行时,如 Go runtime) |
| 资源隔离性 | 完全隔离(独立内存空间) | 共享进程的内存空间(堆、全局变量) | 完全共享所属线程/进程的内存空间 |
| 上下文切换成本 | 极高(涉及虚拟内存切换、TLB刷新) | 较高(内核态切换,需保存寄存器) | 极低(仅保存少量寄存器,用户态切换) |
| 内存占用 | 很大(通常 GB 级虚拟内存) | 中等(MB 级默认栈空间) | 极小(Go 初始仅 2KB,可动态扩容) |
| 创建/销毁成本 | 极重(需 fork 系统调用) | 较重(需 pthread_create 系统调用) | 极轻(纯内存分配,无系统调用) |
| 崩溃影响 | 互不影响(隔离) | 导致整个进程崩溃(共享内存) | 导致所在线程/进程崩溃 |
| 并发模型 | 仅用于多核并行(重量级) | 内核级抢占式调度 | 用户态协作式/多对多调度(GMP 模型) |
在 Go 语言的 GMP 模型中,三者的关系是:
进程是操作系统分配的大房子(内存空间)。
线程(M)是房子里正在干活的正式工人(操作系统线程),由内核派发工资(CPU 时间片)。
Goroutine(协程)是工人手里不断处理的具体任务单。Go 调度器(P)负责决定让哪个任务单交给哪个工人去执行。
一句话总结:进程管“地”(资源),线程管“人”(内核执行),协程管“活”(用户态任务)。Go 的高并发就是用极其廉价的协程任务,巧妙地挂载在少量昂贵的线程工人身上,从而榨干 CPU 性能。