方案对比
- asyncio(原生协程)
- 适用:高并发I/O密集
- 有点:单线程事件循环,内存占用低生态完善,提升调度效率
- 不要阻塞事件循环
- multiprocessing(多进程)
- 适用:CPU密集或者绕开GIL
- 优点:每个进程独立GIl,线性扩展到核心数;与WSGI/gunicorn天然匹配
- 成本:IPC/序列化开销,内存占用高,小任务要批处理或者合并,减少进程间的通信
- gevnet(协做式协程+monkey patch)
- 适用:老项目,阻塞I/O库比较多时的“半异步化”
- 有点:迁移成本低
- 风险:猴子补丁可能引入隐蔽兼容问题;调试和可观测性相对asyncio弱;
GIL何时真成瓶颈
- 纯python CPU密集:如大模型正则/压缩/加解密/序列化/图像处理/打分模型前后处理。单进程下QPS不随并发提升,CPU占用100%但延迟上升。
- C扩展不释放GIL的库:如部分JSON压缩库老版本。换释放GIL的实现(例如orjson,xxhash,zstandard)或多进程
- 事件循环里重做CPU:asyncio线程仍受GIL约束,会把loop卡主
- 多线程错误使用:现成增加不提速,反而上下文切换更过。
非瓶颈的典型:网络IO,数据库IO,文件IO,这类用asyncio最合适
直接背的精简版
- IO密集走asyncio + 全链路异步;CPU密集走多进程/人物队列;
- GIL只在纯Python CPu 密集才卡;IO密集不怕
- 关键是:别再时间循环里做重CPU,用能释放GIL的库或者多进程;控制并发,连接池,超时和背压,有压测数据与告警做容量规划
GIL的影响范围
- 多线程:所有python现成共享一个GIL。即时你开了10个线程,任意时刻也只有1个线程在跑python解释器的代码。
- 多进程:每个进程有自己的GIl,互不影响,因此multiprocessing能绕开GIL
I/O密集场景为什么“GIL”
- IO操作(网络,磁盘,数据库等)本质上是阻塞系统调用。在做IO时,解释器会主动释放GIL,让别的现成可以运行。
def worker():
for _ in range(1000):
requests.get("http://www.baidu.com")
# 现成A在等网络数据时,GIL被释放,线程B就能继续跑了
# 所以你会发现多线程抓网页,文件下载这种IO密集的人物,python多线程是能并行跑的,效果很好
CPU密集场景为什么“卡在GIL”
- CPU密集型人物(数学计算、循环解析、加解密)会长时间运行纯python字节码,几乎没有IO,也就不会主动释放GIL
def cpu_task():
for _ in range(100**8):
x = 12345 * 67890
# 现成A会一直站着GIL,线程B想跑也得等
# 结果就是N个线程和1个线程的速度差不多,CPU占用率最多一个核满载
# 这个时候就是GIL的瓶颈
为什么需要GIL
- CPython实现细节:python的内存管理依赖引用技术。每个对象的引用计数随时可能被不同现成修改
- 简化实现:如果没有锁,就要在所有对象操作上面加细粒度锁,代价很高、实现复杂
- 引入GIL,保证同一时刻只有一个线程执行字节码,这样能避免对象引用计数竞争,降低了内存管理和解释器实现的复杂度
GIL是一种历史拖鞋,为了让CPython实现更简单、更安全!
1万+

被折叠的 条评论
为什么被折叠?



