开篇导语
在真实项目中,Python 多进程是常用方案,用来提升任务处理效率。但你是否遇到过这样的情况:代码在本地运行正常,一上传到 Linux 服务器就卡死,CPU 却显示 0%,进程不退出? 本文结合真实生产经验,带你分析原因,并给出最终可复用解决方案,避免踩坑浪费时间。 本文属于【真实工程踩坑录】系列第一篇,后续还有更多实战案例。
一. 问题现象
场景:
-
服务器:CentOS 7 / Ubuntu 22
-
Python 版本:3.10
-
代码功能:批量处理文件,使用 multiprocessing.Pool 并行
现象:
-
程序启动后不报错
-
CPU 占用极低
-
进程无法退出,任务一直挂起
示例:
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 user 20 0 123m 10m 8m S 0.0 0.1 0:01.23 python
二. 网上常见方案及问题
常见解决方式:
from multiprocessing import Pool
def task(x):
return x * x
if __name__ == "__main__":
with Pool(4) as p:
print(p.map(task, range(10)))
问题:
-
这段代码在本地 Windows / macOS 可以正常跑
-
但在 Linux 服务器上,尤其是 使用 spawn 或 forkserver 启动模式,可能出现卡死
原因:
-
Linux 默认使用 fork,而 fork 在多线程 + 文件句柄复杂时容易挂起
-
子进程未能正确继承资源或父进程阻塞导致进程卡死
三. 真正原因分析
1. fork vs spawn
-
fork 会直接复制父进程内存空间
-
如果父进程含有线程、数据库连接、文件句柄,子进程可能死锁
2. 资源未关闭 / 共享锁
- Pool 创建子进程时,父进程中未关闭的文件 / socket 可能导致阻塞
3. Linux 特有差异
-
Python 3.8+ 默认 macOS spawn,Linux fork
-
导致本地测试正常,服务器挂起
四. 最终可用方案
import multiprocessing as mp
import os
def task(x):
print(f"Process {os.getpid()} working on {x}")
return x * x
def main():
# 强制使用 spawn 启动模式,保证跨平台一致
ctx = mp.get_context("spawn")
with ctx.Pool(4) as pool:
results = pool.map(task, range(10))
print("Results:", results)
if __name__ == "__main__":
main()
说明:
-
使用 mp.get_context(“spawn”) → 避免 fork 死锁
-
子进程可以正常启动,CPU 占用合理
-
适合 Linux 服务器 + Python 3.8+
五. 生产注意事项
1. 数据库 / 文件句柄
-
在多进程启动前,不要提前打开连接
-
子进程内部新建资源,避免共享
2. 日志处理
- 避免父进程持有锁,使用 multiprocessing.Queue + logging
3. 调试技巧
-
使用 ps -ef | grep python 查看子进程状态
-
添加 print 或日志,确认子进程启动
📌【真实工程踩坑录 系列导航】
01|Python 多进程在 Linux 服务器卡死的真正原因 02|MyBatis 批量更新失败?问题根本不在 SQL 03|我用 100 行 Python,替代了每天 2 小时的重复工作 04|一次线上内存暴涨事故,最后发现是一个 list 05|for 循环 vs 列表推导式,性能差距我测给你看 06|别再教新手这样写 Python 了,在公司是会被重构的 07|写了 5 年 Python,我最后留下了这 7 条工程习惯
👉 点击专栏可查看完整系列,按顺序阅读最佳
网硕互联帮助中心




评论前必须登录!
注册