threading受gil限制无法利用多核,multiprocessing通过独立进程和gil实现真正并行;process适合长期任务,pool适合批量计算;参数返回值需可序列化;共享状态须用value/array/queue等机制。

为什么threading跑不满多核,而multiprocessing可以
Python 的 GIL(Global Interpreter Lock)会强制同一时刻只有一个线程执行 Python 字节码,哪怕你开了 8 个 threading.Thread,CPU 使用率也卡在单核水平。这不是 bug,是 CPython 解释器为内存管理安全做的权衡。multiprocessing 绕过了它——每个进程有独立的解释器和独立的 GIL,自然能真正并行。
但要注意:进程启动开销比线程大,IPC(进程间通信)成本高,不适合频繁交互或轻量计算任务。
multiprocessing.Process vs multiprocessing.Pool 怎么选
直接用 Process 更灵活,适合长期运行、状态隔离强的子任务;Pool 更适合“一批输入 → 一批输出”的批量计算场景,自动管理进程数、队列、结果收集。
- 用
Process:需要控制生命周期、共享状态少、子进程要持续监听或维护资源(如数据库连接) - 用
Pool:比如对一个大列表做 CPU 密集型映射,如[heavy_computation(x) for x in data]→ 改成pool.map(heavy_computation, data) -
Pool默认进程数是os.cpu_count(),但若任务含 I/O 或内存吃紧,手动设小点(如Pool(4))反而更稳
传参和返回值有哪些限制
multiprocessing 底层靠序列化(默认 pickle)在进程间传递数据,所以参数和返回值必须是可 pickle 的对象。
常见踩坑点:
- 不能传 lambda 函数、嵌套函数、类实例方法(除非定义在模块顶层)
- 不能传打开的文件句柄、数据库连接、
threading.Lock等非可序列化对象 - 大数组建议用
multiprocessing.shared_memory(Python 3.8+)或numpy.ndarray配合mp.Array,避免反复拷贝 - 返回值太大时(如 GB 级 list),
Pool.map可能卡住或爆内存;改用Pool.imap流式获取结果
如何安全地共享状态或通知主进程
进程间不共享内存空间,所以全局变量、类属性修改不会跨进程生效。真要共享,得用专门机制:
- 用
multiprocessing.Value或multiprocessing.Array共享基础类型(int/float/ctypes 类型) - 用
multiprocessing.Queue或multiprocessing.Pipe传递消息,Queue线程/进程安全,Pipe更快但只支持两点通信 - 避免用
Manager()——它启后台服务进程代理对象,延迟高、易成瓶颈,仅在必须共享 dict/list 等复杂结构时考虑 - 子进程异常默认静默,主进程不会感知;加
process.error捕获或用pool.apply_async(..., error_callback=...)主动处理
真正难的不是启动多进程,而是厘清哪些数据必须复制、哪些可以共享、哪些该用消息驱动——边界模糊时,往往说明任务拆分本身不够正交。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











