默认线程池使用无界队列,任务持续堆积致内存暴涨;未复用executor导致线程数失控;未await Future引发泄漏;CPU密集型任务误用线程池会因GIL竞争而低效。

run_in_executor 默认线程池是无界队列
默认调用 loop.run_in_executor(None, func) 时,asyncio 会懒加载一个全局的 ThreadPoolExecutor,其底层使用的是 queue.Queue(无界队列)。任务 submit 后不阻塞,但会持续堆积在队列里,直到线程池实际消费。如果上游协程提交速度远大于线程处理速度(比如高频 HTTP 请求触发大量文件读取),队列会无限增长,内存持续上涨,最终表现为“线程池耗尽”——实际不是线程没了,而是任务积压、资源卡死。
未复用 executor 导致线程数失控
每次传入新创建的 ThreadPoolExecutor 实例(如 run_in_executor(ThreadPoolExecutor(max_workers=4), func)),又不显式 shutdown(),会导致:
- 每个 executor 启动独立线程池,线程数叠加(例如每秒新建一个 4-worker 池,10 秒后就有 40 个空闲线程)
- 线程对象无法被 GC 回收,
threading.active_count()持续升高 - 系统级文件描述符、栈内存等资源缓慢泄漏,
ls -l /proc/可能发现异常增长/fd | grep socket
忘记 await 或错误处理导致 Future 泄漏
run_in_executor 返回的是 asyncio.Future,必须 await 才能释放关联状态。常见陷阱包括:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 直接丢弃返回值:
loop.run_in_executor(None, sync_func)没接变量,Future 对象仍存活,内部引用链阻止 GC - 在异常路径中漏掉 await:比如 try/except 里只处理了业务异常,却没确保 Future 被 await 或 cancel
- 把 Future 存进列表但永不 result() 或 done() 检查,形成强引用闭环
CPU 密集型任务误用线程池
用默认 ThreadPoolExecutor 处理 CPU 密集型同步函数(如 numpy.linalg.svd、json.loads 大字符串),虽不阻塞事件循环,但会因 GIL 导致线程间严重竞争。表现是:
- 线程池满负荷运转,
active_count()高但 CPU 利用率低 - 任务排队延迟激增,
_work_queue.qsize()持续攀升 - 看似“耗尽”,实则是线程全卡在 GIL 等待,无法并行计算
这种情况该换 ProcessPoolExecutor,而不是加线程数。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










