计算机的核心是 CPU,它负责执行计算任务;操作系统像一个“总管”,负责管理硬件资源、分配内存、调度任务;应用程序则是运行在操作系统之上的具体功能程序。
一句话总结进程线程协程: 进程是资源分配的基本单位,线程是 CPU 调度的基本单位,协程是用户态中更轻量的任务切换方式。
如果用一个通俗类比来理解:
- 进程像一家公司:有自己的办公楼、资产、员工档案和规章制度。
- 线程像公司里的员工:多个员工可以在同一家公司里共同使用会议室、电脑、文件等资源。
- 协程像员工手里的待办事项:员工遇到“等回复、等文件、等网络请求”时,可以先放下当前事项,转去处理另一个事项,等条件满足后再回来继续。
进程
进程是程序的一次运行过程。程序本身只是静态的代码文件,只有当它被操作系统加载并开始执行时,才会形成进程。
比如,磁盘上的微信、浏览器、IDE 都是程序;当我们双击打开浏览器后,操作系统会为它分配内存、文件句柄、网络连接等资源,这个正在运行的浏览器就是一个进程。
进程的组成
进程通常由三部分组成:
- 程序:要执行的指令集合,也就是“做什么”。
- 数据集合:程序运行时需要的数据和工作区。
- 进程控制块 PCB:操作系统用来描述和管理进程的数据结构,记录进程 ID、状态、优先级、寄存器信息、内存信息、打开的文件等。
可以把 PCB 理解为进程在操作系统里的“身份证 + 档案袋”。操作系统不是直接凭感觉管理进程,而是通过 PCB 来知道每个进程是谁、在哪里、运行到哪一步、接下来该不该继续运行。
进程的特点
- 动态性:进程是程序的一次执行过程,有创建、运行、等待、结束等生命周期。
- 并发性:多个进程可以在同一段时间内交替执行,看起来像同时运行。
- 独立性:不同进程通常拥有相互隔离的内存空间,一个进程不能随便访问另一个进程的内存。
- 结构性:进程由程序、数据和 PCB 等内容组成,不只是代码本身。
进程的优点和代价
进程之间相互隔离,所以稳定性比较好。一个浏览器标签页或一个独立程序崩溃时,通常不会直接破坏另一个进程的数据。
但隔离也意味着代价更高:创建进程、销毁进程、进程之间切换都比较耗费资源;进程之间如果要通信,也不能像访问普通变量那样简单,往往需要管道、消息队列、Socket、共享内存等进程间通信方式。
线程
线程是进程中的一个执行流,是 CPU 调度和分派的基本单位。一个进程至少有一个线程,也可以有多个线程。
如果把进程看作一家公司,那么线程就是公司里的员工。公司提供办公楼、资料库、打印机等公共资源;员工负责具体干活。多个员工共享公司资源,但每个员工也有自己的工作进度、手头任务和记忆。
线程和进程的关系
一个进程可以包含多个线程,同一个进程中的线程共享该进程的大部分资源,例如:
- 代码段
- 堆内存
- 全局变量
- 打开的文件
- 网络连接
但每个线程也有自己独立的内容,例如:
- 线程 ID
- 程序计数器 PC
- 寄存器
- 栈空间
简单来说:
进程提供资源容器,线程负责真正执行代码。
多线程的好处
多线程可以让一个程序同时处理多个任务。例如:
- 浏览器一边渲染页面,一边下载资源,一边响应用户点击。
- 聊天软件一边接收消息,一边发送文件,一边更新界面。
- 后端服务一边处理请求 A,一边处理请求 B。
多线程尤其适合把任务拆开处理,使程序响应更快、吞吐量更高。
多线程的问题
线程之间共享同一个进程的内存空间,因此通信比进程更方便,但也更容易出问题。
比如两个线程同时修改同一个变量:
线程 A:读取 count = 0,准备加 1线程 B:读取 count = 0,准备加 1线程 A:写回 count = 1线程 B:写回 count = 1两个线程都执行了加 1,但最后结果却是 1,而不是 2。这就是典型的并发安全问题。
所以多线程编程经常需要使用锁、信号量、原子变量、线程安全队列等机制,避免多个线程同时修改共享数据造成错误。
协程
协程可以理解为比线程更轻量的“用户态任务”。它不是由操作系统内核直接调度,而是由程序自身或语言运行时进行调度。
线程的切换通常由操作系统决定,属于抢占式调度;协程的切换通常发生在代码主动让出执行权的时候,比如遇到 await、yield、挂起函数、异步 IO 等。
如果说线程是“操作系统安排员工轮流使用 CPU”,那么协程更像是“员工自己安排手里的多个待办事项”:
- 当前事项需要等待网络返回,就先挂起。
- 趁等待期间去处理另一个事项。
- 等网络返回后,再从刚才挂起的位置继续执行。
协程的特点
- 轻量:创建和切换成本通常低于线程。
- 用户态调度:很多情况下不需要进入操作系统内核完成切换。
- 适合 IO 密集型任务:比如网络请求、文件读写、数据库访问等大量等待场景。
- 写法接近同步代码:相比传统回调,协程能让异步逻辑更直观。
协程要注意的点
协程并不等于多核并行。多个协程可能只运行在一个线程上,它们是在等待和恢复之间切换,看起来可以同时处理很多任务,但同一时刻真正执行代码的仍然可能只有一个。
如果任务是大量计算,例如图片压缩、视频编码、复杂算法计算,只使用单线程协程并不能让 CPU 多核一起工作。这类 CPU 密集型任务通常需要多线程、多进程或专门的并行计算方案。
任务调度
大部分现代操作系统,比如 Windows、Linux,都会使用抢占式调度。CPU 会把执行时间切成很短的时间片,操作系统内核根据调度策略决定哪个线程获得下一个时间片。
一个线程运行一小段时间后,可能会发生以下情况:
- 时间片用完,被操作系统暂停。
- 主动等待 IO,例如读文件、请求网络。
- 被更高优先级的线程抢占。
- 执行完成并退出。
当操作系统暂停一个线程时,需要保存它当前的执行现场,例如寄存器、程序计数器、栈信息等;之后恢复它时,再把这些信息加载回来,让线程从之前停止的地方继续执行。这个过程叫做上下文切换。
上下文切换让多个任务可以交替执行,但它本身也有成本。因此线程不是越多越好,线程过多会让 CPU 花大量时间在切换上,反而降低性能。
并发和并行
并发和并行经常一起出现,但含义不同。
并发强调“同一段时间内处理多个任务”。这些任务可能是交替执行的,不一定真的在同一瞬间运行。
并行强调“同一时刻真的有多个任务在运行”。这通常需要多核 CPU 支持。
通俗类比:
- 并发:一个人同时煮饭和烧水。实际动作是切换进行的,比如先淘米,再开水壶,再回来洗菜。
- 并行:两个人一起干活。一个人煮饭,另一个人烧水,两个任务真的同时推进。
单核 CPU 也可以并发,因为它可以在多个任务之间高速切换;多核 CPU 才能真正并行,因为多个核心可以在同一时刻执行不同任务。
总结
| 对比项 | 进程 | 线程 | 协程 |
|---|---|---|---|
| 所属层级 | 操作系统层面 | 操作系统层面 | 用户态或语言运行时层面 |
| 核心作用 | 资源分配的基本单位 | CPU 调度的基本单位 | 更轻量的任务切换方式 |
| 资源拥有 | 拥有独立内存和系统资源 | 共享所属进程的资源 | 通常共享所属线程的资源 |
| 切换成本 | 高 | 中 | 低 |
| 通信方式 | 需要进程间通信机制 | 可通过共享内存通信 | 通常通过异步结果、Channel、队列等方式协作 |
| 稳定性 | 隔离性强,一个进程崩溃不容易影响其他进程 | 一个线程崩溃可能影响整个进程 | 依赖所在进程和线程 |
| 适合场景 | 稳定隔离、独立服务、多进程架构 | 需要 CPU 调度的并发任务 | 大量 IO 等待、异步任务编排 |
简单来说:进程管资源,线程管执行,协程管等待时的高效切换。
- 进程是“资源容器”:操作系统给程序分配资源后,程序的一次运行就是进程。
- 线程是“执行工人”:线程在进程里真正执行代码,是 CPU 调度的基本单位。
- 协程是“轻量待办”:协程在用户态主动挂起和恢复,适合把大量等待型任务组织得更清楚。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时