Python 多线程 vs 多进程对比
作者:XD / 发表: 2025年7月17日 05:02 / 更新: 2025年7月17日 05:03 / 编程笔记 / 阅读量:11
🎯 背景
你是否也遇到过这种情况:
明明 CPU 是多核的,代码也用了
ThreadPoolExecutor
开了很多线程,结果速度不仅没有变快,反而变慢了……
最近,我就踩了这个坑:处理 GGUF 模型文件中的张量数据时,原始多线程版本耗时 300 分钟,而换成多进程后,只用了 3 分钟,性能差距高达 100 倍!
🧱 为什么多线程在 Python 中这么慢?
罪魁祸首就是 Python 的 GIL(全局解释器锁)。
GIL 是什么?
在 CPython(最常用的 Python 解释器)中,GIL 是一个互斥锁,用于保证任何时刻只有一个线程能执行 Python 字节码。
GIL 的影响:
- ✅ I/O 密集型任务(如网络请求、文件读写):线程会在等待时释放 GIL,效率还不错。
- ❌ 计算密集型任务(如矩阵计算、数据转换):所有线程争抢 GIL,根本没法并行,性能下降!
✅ 正确做法:使用多进程
GIL 只存在于单个进程中,多进程意味着每个子进程有自己的 GIL,可以在多个 CPU 核心上并行运行。
🧪 实战代码对比
🚫 多线程版本(ThreadPoolExecutor)
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm
def process_tensor(tensor):
# 模拟计算密集型任务
return tensor.name, tensor.data.sum()
if __name__ == '__main__':
tasks_to_process = [...] # 张量任务列表
results = []
with ThreadPoolExecutor(max_workers=8) as executor:
futures = {
executor.submit(process_tensor, t): t.name
for t in tasks_to_process
}
for future in tqdm(as_completed(futures), total=len(futures), desc="Threading"):
results.append(future.result())
⏱️ 耗时约 300 分钟,CPU 利用率低。
🚀 多进程版本(ProcessPoolExecutor)
from concurrent.futures import ProcessPoolExecutor, as_completed
from tqdm import tqdm
def process_tensor(tensor):
return tensor.name, tensor.data.sum()
if __name__ == '__main__':
tasks_to_process = [...] # 张量任务列表
results = []
with ProcessPoolExecutor() as executor: # 默认使用所有核心
futures = {
executor.submit(process_tensor, t): t.name
for t in tasks_to_process
}
for future in tqdm(as_completed(futures), total=len(futures), desc="Multiprocessing"):
results.append(future.result())
⚡ 耗时仅 3 分钟,CPU 多核并行利用率高。
✅ 结果输出(通用)
for name, result in results:
print(f"{name}: {result}")
📊 对比总结
| 类型 | 实现方式 | 预计耗时 | 核心利用率 |
| 多线程 | ThreadPoolExecutor | \~300 分钟 | ❌ 低,受 GIL 限制 |
| 多进程 | ProcessPoolExecutor | \~3 分钟 | ✅ 高,真并行 |
📌 何时用线程?何时用进程?
| 任务类型 | 推荐方式 | 原因 |
| CPU 密集型 | ✅ 多进程 | 避免 GIL,真正并行 |
| I/O 密集型 | ✅ 多线程 | 阻塞时释放 GIL,提高吞吐 |
🛠️ 小贴士
tqdm(as_completed(...))
让你轻松跟踪任务完成进度。- 多进程任务函数必须能被 Pickle 序列化。
- 若需支持错误捕获、失败重试、进度恢复,可结合日志模块扩展。
🧠 最后总结
Python 并发并不神秘,关键在于:
- 辨别任务类型(CPU-bound vs I/O-bound)
- 选对模型(Threading vs Multiprocessing)
一旦用对方式,性能可不是提升一点,而是飞跃几十倍、甚至百倍!