Python subprocess 和 multiprocessing 有什么区别?什么时候用哪个?
简化版
subprocess 用来启动和控制外部程序,例如执行 ffmpeg、git、系统命令;multiprocessing 用来在 Python 中创建子进程并运行 Python 函数,常用于绕过 GIL 做 CPU 密集计算。前者关注外部命令,后者关注 Python 任务并行。
详细版
如果你要调用现成命令行工具,用 subprocess.run()、Popen();如果你要把 Python 函数分发到多个进程执行,用 multiprocessing.Process、Pool 或 ProcessPoolExecutor。
import subprocess
subprocess.run(["git", "status"], check=True)
from multiprocessing import Pool
with Pool() as pool:
print(pool.map(cpu_work, range(10)))
安全上,subprocess 要避免 shell=True 拼接用户输入;性能上,multiprocessing 有序列化和进程通信成本,不适合很小的任务。
完整版教学
一、两个模块解决的问题不同
subprocess 是“从 Python 调外部程序”。外部程序可以是任何可执行文件,不一定是 Python。multiprocessing 是“用多个 Python 进程跑 Python 代码”。它们都创建进程,但抽象层不同。
subprocess:
Python -> ffmpeg/git/curl
multiprocessing:
Python parent -> Python child -> run function
这一区分是面试回答的主线。
二、subprocess 适合什么场景
调用系统工具、脚本、编译器、压缩工具、媒体处理工具时用 subprocess。它能控制参数、环境变量、工作目录、标准输入输出和退出码。
result = subprocess.run(
["python", "--version"],
capture_output=True,
text=True,
check=True,
)
print(result.stdout)
如果命令失败,check=True 会抛异常。生产中要记录命令、退出码、stderr,并设置超时,避免外部命令卡死。
三、multiprocessing 适合什么场景
CPU 密集型 Python 函数可以用多进程并行,因为每个进程有自己的解释器和 GIL。例如图像处理、压缩、特征计算、批量数据转换。
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor() as ex:
results = list(ex.map(parse_file, files))
但任务参数和结果需要 pickle 序列化,进程启动也有成本。100 万个 1ms 小任务直接丢进进程池,调度和序列化成本可能超过计算本身。
四、shell=True 为什么危险
subprocess.run("rm -rf " + user_input, shell=True) 这类拼接很危险。用户输入可能注入额外命令。更安全做法是传参数列表,并避免 shell 解释。
subprocess.run(["grep", pattern, filename], check=True)
| 写法 | 风险 |
|---|---|
| 字符串拼接 + shell=True | 命令注入 |
| 参数列表 + shell=False | 更安全 |
| 固定命令 + 校验参数 | 推荐 |
调外部命令时,用户输入必须当“参数”传,不要当“命令文本”拼。
五、输出和超时怎么处理
外部命令可能输出很多内容。如果使用 Popen 但不消费 stdout/stderr,管道缓冲区可能被写满,导致子进程阻塞。简单场景用 subprocess.run(capture_output=True, timeout=...)。
subprocess.run(
["tool", "arg"],
timeout=30,
capture_output=True,
text=True,
)
长时间运行的命令要流式读取日志,或者让它直接输出到文件/平台日志,避免一次性把几百 MB 输出读进内存。
六、怎么做选择
如果已有成熟命令行工具,subprocess 通常最直接;如果核心逻辑是 Python 函数,且需要 CPU 并行,选 multiprocessing 或 ProcessPoolExecutor。
| 场景 | 选择 |
|---|---|
调 ffmpeg 转码 | subprocess |
调 git 查询状态 | subprocess |
| 并行计算图片特征 | multiprocessing |
| 并行解析 Python 对象 | ProcessPoolExecutor |
还要考虑部署环境:外部命令必须存在于机器或镜像中;多进程则要考虑启动方式、pickle、内存占用。
七、常见误区与追问
- 误区:subprocess 和 multiprocessing 是一回事。 都会创建进程,但一个调外部程序,一个跑 Python 函数。
- 误区:shell=True 更方便所以推荐。 它有命令注入风险,默认应避免。
- 误区:多进程一定更快。 序列化、进程启动和 IPC 都有成本,小任务可能更慢。
- 追问:如何拿到外部命令输出?
capture_output=True或Popen管道读取。 - 追问:外部命令卡住怎么办? 设置 timeout,必要时终止进程树。
- 追问:ProcessPool 传 lambda 可以吗? 通常不行,目标函数和参数要可 pickle。
- 追问:调用外部命令如何传环境变量? 用
env参数传入明确环境。
八、加强记忆
subprocess 记成“调外部命令”,multiprocessing 记成“跑 Python 函数并行”。前者重点是参数安全、退出码、stdout/stderr、timeout;后者重点是 GIL、pickle、启动方式和任务粒度。先问“我要执行的是外部程序还是 Python 函数”,选择就清楚了。