「FastAPI & SQLAlchemy 实战」系列共 10 篇,从协程基础讲到数据库接口。这是第 1 篇。
结论:I/O 密集型任务优先用协程,CPU 密集型任务用多进程,线程只适合少量阻塞调用或必须使用同步库的场景。
一、三者怎么选
| 进程 | 线程 | 协程 | |
|---|---|---|---|
| 调度者 | 操作系统 | 操作系统 | 程序自己(事件循环) |
| 内存 | 独立地址空间 | 共享进程内存 | 共享线程内存 |
| 切换成本 | 高 | 中 | 极低 |
| 并发量 | 几十个 | 几百到几千 | 几万 |
| 适合 | CPU 密集 | 少量阻塞 I/O | 高并发 I/O |
二、线程为什么扛不住高并发 I/O
- 切换开销:几千个线程之间切换,CPU 大量时间花在上下文切换上;
- 同步复杂:共享内存需要锁,锁用不好就死锁;
- 阻塞浪费:线程等待网络时被挂起,但线程本身仍占资源,创建数量也有上限。
三、协程的优势
- 由程序自己调度,
await时主动让出 CPU; - 创建成本低,一个线程可以挂几万个协程;
- 同一时刻只有一个协程在跑,不需要锁保护共享数据;
- 等待 I/O 时切换到其他协程,不浪费 CPU。
注意:协程是单线程并发,不是并行。CPU 密集型任务不会因为用了协程就变快。
四、一个直观对比
10 个网络请求,每个耗时 1 秒:
- 串行
await:约 10 秒; - 线程池:受池大小限制,通常 1–2 秒,但线程数有上限;
- 协程并发:约 1 秒,几千个请求也能挂在同一个线程上。
# 线程:每个请求占一个线程
# 协程:等待 I/O 时让出,事件循环去跑其他协程
五、什么时候别用协程
- CPU 密集:用多进程,协程帮不上忙;
- 必须用同步库(
requests、pymysql):它会阻塞整个事件循环,用asyncio.to_thread()隔离; - 任务之间有大量共享状态:协程虽然不用锁,但逻辑复杂度不一定低。
六、选择顺序
- CPU 密集 → 多进程;
- I/O 密集且有异步库 → 协程;
- I/O 密集但只有同步库 → 线程池,或用
asyncio.to_thread隔离。
# 错误:async def 里 time.sleep(1),整个事件循环停 1 秒
# 正确:await asyncio.sleep(1)
下一篇
下一篇讲 async def、await 和事件循环:协程到底是怎么被调度起来的。

AI项目博客






