纯计算密集型任务应使用 multiprocessing.pool 或 processpoolexecutor 以绕过 gil 实现真正并行;threading.thread 因 gil 限制仅能并发无法并行;注意函数可 pickle、加 if name == '__main__': 保护、合理设置 max_workers。

对纯计算密集型任务,直接用 multiprocessing.Pool 或 concurrent.futures.ProcessPoolExecutor 是最有效的方式;它不靠优化代码逻辑,而是靠启动独立进程绕开 GIL,让每个 CPU 核心真正并行干活。
为什么 threading.Thread 在计算任务里几乎没用
CPython 的 GIL 强制所有线程轮流执行 Python 字节码。哪怕你有 16 核 CPU,4 个 threading.Thread 跑矩阵乘法或斐波那契,也只在一个核上串行抢锁——CPU 使用率顶多 100%,但其他核全空转。典型现象包括:
- 开 4 个线程处理各耗时 1 秒的计算,总耗时仍接近 4 秒,甚至更长(线程切换 + GIL 争抢拖慢节奏)
- 任务管理器显示“仅一个核心满载,其余闲置”,而你明明写了多线程
- 误以为开了线程就等于并行,实际只是并发(concurrent),不是并行(parallel)
Pool.map 和 Pool.imap_unordered 怎么选
两者都用于批量分发任务,但行为差异直接影响结果收集逻辑和内存占用:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
-
pool.map(func, iterable):阻塞等待全部完成,返回完整结果列表;适合结果量小、需严格顺序处理的场景(比如后续要按输入顺序拼接数组) -
pool.imap_unordered(func, iterable):返回迭代器,结果按完成先后返回(不保序),内存友好;适合流式处理或想边算边用结果(比如实时写入文件、逐条入库) - 别用
pool.apply_async手动管理callback+.get()——容易漏.get()导致子进程卡住,或忘记pool.close()/pool.join()
Windows 下不加 if __name__ == '__main__': 就会炸
Windows 和部分 spawn 启动方式下,子进程会重新导入主模块。如果不加保护,就会递归创建新进程,直到系统资源耗尽或报错:
- 错误信息通常是:
RecursionError: maximum recursion depth exceeded或直接卡死 - 即使在 Linux/macOS 上显式调用
mp.set_start_method('spawn'),同样需要该保护 - 所有
multiprocessing启动代码(Process().start()、Pool()等)都必须放在这个块内 - 漏掉这一行,程序可能跑一半就崩,或者静默退化成单进程——得靠
psutil看进程数或加日志确认是否真并行
函数和参数必须可 pickle,否则静默失效
进程间通信靠 pickle 序列化,所以传给 pool.map 的函数和参数必须可序列化:
- 不能是
lambda、嵌套函数、类实例方法、functools.partial包了不可序列化对象的函数 - 函数必须定义在模块顶层,且能被子进程 import(不能定义在
if __name__ == '__main__':块里) - 小任务 + 高频调用时,进程创建/序列化开销可能吃掉并行收益;建议单次任务耗时 ≥ 100ms 再上进程池
- 默认
max_workers是os.cpu_count(),但若任务本身已占满内存(如每个进程加载大模型),开满核会触发频繁 swap;建议先设为min(4, os.cpu_count())起步
真正容易被忽略的是:即使你写了 Pool,如果函数里偷偷用了不可 pickle 的对象(比如某个自定义类的绑定方法),程序不会报错,而是静默退化成单进程执行——得靠 psutil 看进程数或加日志确认是否真并行。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










