核心概念速览
并发 vs 并行
通俗例子:
- 不支持并发/并行: 吃完饭才接电话
- 并发: 停下吃饭去接电话,接完继续吃 (快速切换)
- 并行: 一边吃饭一边打电话 (同时进行)
本质区别:
- 并发: 有处理多个任务的能力,但不一定同时 (单核 CPU 时间片轮转)
- 并行: 能同时处理多个任务 (多核 CPU 真正并行)
同步 vs 异步
- 同步: 等待操作完成才继续 (如:等快递送到才出门)
- 异步: 不等待,继续执行,完成后通知 (如:快递到了发短信通知)
阻塞 vs 非阻塞
- 阻塞: 调用时被操作系统挂起,必须等待
- 非阻塞: 调用后立即返回,不会被挂起
进程 (Process)
定义: 资源分配的最小单位,程序运行的实例。
特点:
- 独立的内存空间、文件描述符、系统资源
- 进程间相互独立,互不干扰
- 切换开销大 (需要保存/恢复完整上下文)
举例: 聊 QQ 和看视频是两个独立的进程
线程 (Thread)
定义: 程序执行的最小单位,也称”微进程”。
特点:
- 共享所属进程的资源 (内存、文件等)
- 切换开销较小 (共享资源,只需切换少量上下文)
- 存在资源竞争问题 (如 Python 的 GIL 锁)
举例: 播放器中,视频画面、声音输出、进度条拖动是不同的线程,它们协同工作让体验流畅。
协程 (Coroutine)
定义: 用户态的轻量级线程,程序自己控制切换。
优点:
- ✅ 极低的切换开销 (用户态切换,无需系统调用)
- ✅ 高并发支持 (单核可支持上万协程)
- ✅ 无需锁机制 (单线程内切换)
缺点:
- ❌ 无法利用多核 (本质是单线程)
- ❌ 阻塞操作会阻塞整个程序
适用场景: I/O 密集型任务 (网络请求、文件读写)
三者对比
| 对比维度 | 进程 | 线程 | 协程 |
|---|---|---|---|
| 定位 | 资源分配单位 | 执行调度单位 | 用户态轻量级线程 |
| 调度者 | 操作系统 | 操作系统 | 程序自身 |
| 资源隔离 | ✅ 独立内存空间 | ❌ 共享进程资源 | ❌ 共享线程资源 |
| 并行能力 | ✅ 真并行 (多核) | ⚠️ 受 GIL 限制 | ❌ 单线程内切换 |
| 切换代价 | 高 (内核态) | 中 (系统调用) | 极低 (用户态) |
| 适用场景 | CPU 密集型 | I/O 密集型 | 高并发 I/O |
形象比喻:工厂模型
- 程序 = 设计图纸
- 进程 = 独立工厂 (有自己的土地、机器、电力)
- 线程 = 工厂里的工人 (共享工厂资源)
- GIL (Python) = 工厂机器室的唯一钥匙 (同一时刻只有一个工人能操作)
- 协程 = 聪明的工人 (等待烤箱时主动去做其他事,烤好了再回来)
最佳实践
为更直观地理解 Python 中的进程、线程与协程,使用“工厂”比喻:
设计图 → 程序(Program)
程序就像工厂的设计蓝图,尚未运行时只是计划。工厂 → 进程(Process)
蓝图投入运行后,操作系统会分配土地(内存)、机器(CPU 时间片)、接通电源(系统资源),蓝图变成一座真实的工厂。每座工厂独立,互不干扰。
• 进程 = 被系统赋予资源后、真正“开工”的程序实例。工人 → 线程(Thread)
工厂需要工人来操作机器,工人即线程。工人共享工厂资源,各自负责不同环节(主工人负责调度、记录,其他工人负责运输、组装等)。
• 线程 = 在同一工厂中共享资源的执行单元。钥匙(GIL)→ Python 的执行控制机制
Python 工厂的机器室只有一把钥匙(GIL)。每个工人操作机器前必须拿到钥匙,任意时刻只有一个工人能工作,完成后交回钥匙。这样防止资源竞争。
• GIL = 保证同一时间只有一个工人能操作机器(互斥执行)。厂区调度系统 → 操作系统调度器
工业区(操作系统)同时管理多座工厂(进程),决定谁先用电、谁暂停、何时切换(如 CFS)。
• 调度进程的是操作系统;线程的调度受操作系统 + GIL 影响。工人自我调度 → 协程(Coroutine)
有些工人会自我管理,主动切换任务以提高效率(例如:烘焙时把面团放进烤箱后去揉下一份,烤箱响再回来取)。
• 协程 = 工人自己决定何时切换任务,切换轻量且非抢占式。对比小表
| 概念 | 类比 | 调度者 | 是否并行 | 是否共享资源 | 切换代价 |
|---|---|---|---|---|---|
| 进程 | 工厂 | 操作系统 | ✅ 真并行(多核) | ❌ 否 | 高(内核级) |
| 线程 | 工人 | 操作系统 + GIL | ⚠️ 伪并行(同一时刻一人) | ✅ 是 | 中(系统级) |
| 协程 | 自我调度的工人 | 工人自身 | ❌ 并发(非并行) | ✅ 是 | 极低(用户态) |
完整故事(简述)
一份蓝图(程序)被批准投产后成为工厂(进程)。工厂雇来工人(线程),但机器室只有一把钥匙(GIL),工人们轮流使用机器。工业区调度中心(操作系统)决定哪座工厂先用电。聪明的工人(协程)会在等待 I/O 时切换任务,大幅提高效率。一句话总结
进程 = 工厂(资源容器);线程 = 工人(执行单元);GIL = 唯一钥匙(互斥);操作系统 = 厂区调度;协程 = 自主切换的聪明工人。
(可按需将此节放在 最佳实践 之后或作为附录)
最佳实践
场景选择
| 任务类型 | 推荐方案 | 原因 |
|---|---|---|
| CPU 密集型 | 多进程 | 绕开 GIL,充分利用多核 |
| I/O 密集型 | 多线程/协程 | 等待 I/O 时可切换到其他任务 |
| 高并发 I/O | 协程 | 极低的切换开销,支持海量并发 |
| 混合场景 | 多进程 + 协程 | 进程利用多核,协程提升单核效率 |
代码示例
CPU 密集型:使用多进程
1 | from multiprocessing import Pool |
I/O 密集型:使用协程
1 | import asyncio |
关键原则
- CPU 密集 → 多进程 (计算、循环、数据处理)
- I/O 密集 → 协程 > 线程 (网络、文件、数据库)
- 高性能 → 多进程 + 协程 (充分利用硬件 + 高效调度)