进程 vs. 线程
日常用电脑时,你肯定遇到过这种场景:后台挂着游戏更新,浏览器开了3个不同的编程文档、1个听歌网页,还开着微信摸鱼——这就是典型的多任务并行处理。
今天我们就拆解实现多任务的核心角色:进程、线程,还有现代高并发场景下的“新宠”协程,对比它们的优劣、适用场景,最后附上Python的极简实现代码。
1. 多任务的通用架构:Master-Worker模式
不管是多进程、多线程还是协程,大部分系统实现多任务的底层思路都离不开Master-Worker分工协作:
- Master(监工):负责任务接收、拆分、分配,偶尔也做全局协调
- Worker(工人):接收到任务后立刻执行
- 常规配置:一个Master对应多个Worker
具体到不同实现:
- 多进程:主进程当Master,子进程当Worker
- 多线程:主线程当Master,子线程当Worker
- 协程:主线程/单个进程内的事件循环当Master,协程当Worker
2. 传统双雄:多进程 vs 多线程
多进程模式
我们可以把进程理解为一个独立的“工作车间”:车间有自己的仓库(内存空间)、工具库(文件句柄、网络连接),车间之间完全物理隔离,除非专门修“传送带”(进程间通信,IPC),否则不能随便拿对方的东西。
优点
- 稳定性天花板:某个子车间(子进程)炸了(崩溃),只会收拾它的烂摊子,主车间和其他子车间完全不受影响——这也是早期Apache、Chrome浏览器早期架构用纯多进程的原因
- 彻底利用多核CPU:每个车间可以独占一个CPU核心干活,不受调度限制(Windows/Linux都能做到)
- 绝对的内存隔离:不用担心其他车间“偷拿”或“污染”自己的数据
缺点
- 车间建造成本高:申请独立仓库、工具库都需要时间和系统资源,Windows系统下的创建成本尤其大(是Linux的数倍甚至数十倍)
- 切换成本大:车间主任(CPU核心)从A车间转到B车间,得先把A的所有工具、进度条收好,再把B的拿出来摆好,这个过程叫上下文切换
- IPC比较麻烦:车间之间传文件、数据得用专用的传送带(管道、消息队列、共享内存等),共享内存还要加“安全门”(锁)防止拿错,代码复杂度会上升
多线程模式
线程则是车间里的“工人小组”:所有小组共享同一个仓库、工具库,但每个小组有自己的工作桌、进度条(寄存器、栈空间)。小组之间沟通直接递纸条就行(共享内存),不需要额外的传送带。
优点
- 小组组建快:不需要申请独立仓库,直接在现有车间里划分一块区域当工作桌就行,创建和切换成本远低于进程
- 小组间沟通简单:递纸条(共享全局变量、堆内存)效率极高
- 适合I/O等待场景:比如某个小组在等快递(等网络请求、等磁盘读写),车间主任可以立刻切换到另一个空闲小组干活,CPU不会闲着
缺点
- 稳定性较差:某个小组把车间的主电源碰断了(触发非法内存访问),整个车间(整个进程)都会断电——这也是很多早期Web服务器不敢用纯多线程的原因
- 需要处理“抢仓库”的问题:多个小组同时用同一把螺丝刀(修改同一个全局变量),会把数据改乱,这叫竞态条件,得加“工具使用登记本”(锁)来规范,但锁用多了会让效率下降,甚至出现“死锁”(两个小组各拿对方需要的螺丝刀不放)
- Python的特殊限制:CPython(我们最常用的Python解释器)里有个GIL(全局解释器锁):同一时间只能有一个小组(线程)在车间的“主操作区”(执行Python字节码)干活——这意味着纯Python写的多线程,没法真正利用多核CPU并行处理计算任务
3. 现代服务器的妥协:混合模式
纯多进程或纯多线程都有各自的缺陷,所以主流服务器软件早就用上了“多进程保底+多线程提效”的混合模式:
- Apache:支持三种多处理模块(MPM)
prefork:纯多进程,适合稳定性要求极高的场景worker:多进程打底(每个子进程里开多个线程),平衡了稳定性和资源利用率event:在worker的基础上优化了I/O等待,性能更高
- IIS:默认多线程,但也支持进程隔离模式
- Nginx:走得更远,直接用了单Master+多Worker进程+事件驱动的异步模型,性能碾压Apache和IIS的传统模式
4. 别让“换工人”拖垮效率:任务切换的坑
不管是切换进程、线程,还是后面说的协程,上下文切换本身都是有成本的:
- 保存当前任务的进度条、工具状态
- 准备下一个任务的进度条、工具状态
- 把下一个任务放到CPU核心上开始执行
如果任务开得太多:
- 系统可能会把80%以上的时间花在切换上,只有20%的时间在真正干活
- CPU核心利用率看起来很高,但实际业务处理速度很慢,甚至会出现“系统假死”(鼠标键盘动不了,但后台进程还在跑)
5. 选对人干对活:任务类型与方案匹配
在选择多任务方案前,先搞清楚你的任务是“CPU忙”还是“闲等多”:
计算密集型任务(CPU忙)
特点:
- 大部分时间都在CPU上运算,几乎不闲等
- 典型例子:科学计算、视频编解码、密码学哈希/加密、机器学习训练推理
建议:
- 任务数≈CPU核心数(多1-2个也可以,用来处理突发的系统调度)
- 优先用C/C++/Rust/Golang这些没有全局解释器锁、编译后效率极高的语言
- 如果非要用Python,选多进程(可以绕开GIL),或者用C扩展(比如NumPy、PyTorch底层都是C写的,不受GIL限制)
I/O密集型任务(闲等多)
特点:
- 大部分时间都在“闲等”:等网络请求返回、等数据库读写、等用户输入
- CPU核心利用率很低(可能只有10%-30%)
- 典型例子:Web服务、爬虫、数据库中间件、聊天软件后端
建议:
- 任务数可以适当开多(比如CPU核心数的5-10倍,甚至更多)
- Python/JavaScript这些解释型脚本语言完全够用,开发效率比执行效率重要得多
- 现代趋势是用异步I/O+协程(后面会详细讲),比多线程效率更高、开销更低
6. 高并发场景的新宠:异步I/O+协程
异步I/O
现代操作系统(Linux的epoll、Windows的IOCP、MacOS的kqueue)提供了高效的异步I/O支持:不需要专门开多个线程/进程等待I/O,只需要告诉操作系统“等这个网络请求回来后喊我一声”,然后主线程/单个进程就可以去干别的事了。
典型代表:Nginx、Node.js、Redis(主进程是单线程的,但性能极高)
协程
协程可以理解为“轻量级到极致的工人小组”:所有协程都在同一个线程里,由程序自己写的调度器(事件循环)控制切换,不需要操作系统插手。
切换一个协程的成本有多低?大概是切换线程的千分之一甚至万分之一——只需要保存和恢复几个指针就行。
Python里的协程语法是async/await,配套的标准库是asyncio,还有第三方库gevent(对同步代码更友好)。
优势
- 并发能力拉满:单个线程里可以轻松开几万甚至几十万个协程(多线程最多开几千个就会因为栈空间不够崩溃)
- 几乎没有切换成本
- 不需要处理复杂的线程锁问题(因为只有一个线程在执行协程,同一时间只有一个协程在运行“主操作区”的代码)
7. 一张表搞定选择
8. Python实现示例(极简版)
多进程
运行结果大概是总耗时3s左右(因为三个子进程并行执行)。
多线程
因为是I/O密集型,总耗时也是3s左右(GIL在time.sleep时会释放)。
协程
总耗时同样是3s左右,但资源利用率比多线程高得多。
9. 最后总结
- 先分任务类型:CPU密集型找多进程/C扩展,I/O密集型找协程/异步框架
- 不要过度设计:如果只是简单的后台定时任务,线程池就足够了
- Python特殊情况:永远别忘了GIL的存在
- 关注现代趋势:异步I/O和协程已经是高并发Web服务的主流方案
选择最适合你需求的,而不是看起来“最厉害”的,这才是好的架构设计。

