python多任务应据任务类型选择:i/o密集用threadpoolexecutor(配连接池与合理worker数),cpu密集用processpoolexecutor(函数需顶层定义),异步i/o用asyncio(配aiohttp等异步库)。

Python 多任务并行处理不能靠 threading 简单“开多线程”就完事——CPython 的 GIL 让它对 CPU 密集型任务几乎无效;而盲目上 multiprocessing 又容易因进程开销、数据序列化、共享状态混乱反而变慢。
什么时候该用 concurrent.futures.ThreadPoolExecutor
适合 I/O 密集型任务:HTTP 请求、文件读写、数据库查询。线程切换成本低,GIL 在等待时会释放,实际能并发执行。
常见错误现象:requests.get() 批量调用耗时没下降,甚至更慢——可能因为没设连接池或超时,或者任务数远超系统能稳定维持的 socket 数量。
- 控制并发数别硬写
max_workers=100,一般设为min(32, (os.cpu_count() or 1) + 4)更稳妥 - 务必用
session复用连接(requests.Session()),否则每个get都新建 TCP 连接,开销爆炸 - 避免在
submit()的函数里直接操作全局变量或类实例属性——线程不安全,要用threading.local()或加锁
CPU 密集型任务必须用 concurrent.futures.ProcessPoolExecutor
multiprocessing.Pool 虽然也能用,但 ProcessPoolExecutor 提供更统一的 submit/as_completed 接口,异常传播也更清晰。
使用场景:图像批量处理、数值计算、解析大型 JSON/XML、加密解密等。
- 传入函数必须是模块顶层可导入的(不能是嵌套函数、lambda 或
@lru_cache包裹的函数),否则子进程反序列化失败,报错AttributeError: Can't get attribute ... - 大对象别通过参数传——走
functools.partial预绑定,或改用initializer+ 全局变量(需注意进程间不共享内存) - 返回值会被 pickle,含不可序列化对象(如
threading.Lock、文件句柄)会直接崩溃
asyncio 不是“另一个并行方案”,而是协程调度器
它本身不并行,只并发;适合高吞吐、低延迟的 I/O 场景(比如同时处理几千个 WebSocket 连接),但写法和调试心智负担明显更高。
容易踩的坑:time.sleep(1) 会阻塞整个事件循环,必须换 await asyncio.sleep(1);同步库(如 requests、sqlite3)不能直接 await,得用 loop.run_in_executor 包一层。
- 不要混用
asyncio和threading:比如在async def里开新线程,再试图 await 它——逻辑断裂,难以 debug -
aiohttp替代requests是基本要求;数据库要选asyncpg、aiomysql等原生异步驱动 - 启动方式必须是
asyncio.run(main()),别用loop.create_task()后忘掉loop.run_forever()—— 程序静默退出
真正难的不是选哪个模块,而是判断任务类型、预估数据规模、测量真实瓶颈。一个 time.time() 包裹的基准测试,比所有理论都管用。别让“并行”成为性能优化的第一步,先确认它真是瓶颈所在。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











