Ubuntu下Python多进程处理DataFrame报Too Many Files Open错误
可能的原因
并发进程数仍超出文件描述符承载
你假设500个进程远低于系统上限,但Ubuntu默认软文件描述符限制通常是1024,每个进程至少会占用3个描述符(stdin/stdout/stderr),加上Queue底层管道的描述符,500个进程的总描述符占用会轻松突破1024的限制。手动管理进程与Queue导致资源泄漏
每个Process连接全局Queue时,会创建管道的一端。即使进程结束,若未正确回收Queue的连接资源,可能残留未关闭的文件描述符。此外,你先启动所有500个进程再依次join,这段时间内所有进程的描述符都处于占用状态。业务函数
expandDates存在文件泄漏
如果do stuff环节涉及文件操作(比如读取配置、写入临时文件),但未用with语句或手动关闭文件,会导致每个进程都残留未关闭的文件描述符,累积后触发上限。
调试步骤
检查系统文件描述符限制
运行以下命令查看当前限制:# 查看软限制(当前生效的限制) ulimit -n # 查看硬限制(最大可调整到的上限) ulimit -Hn若软限制是1024,可临时提高:
ulimit -n 4096永久修改需编辑
/etc/security/limits.conf,添加:your_username soft nofile 4096 your_username hard nofile 8192实时监控进程的文件描述符
找到主进程的PID(用ps aux | grep python),然后运行:lsof -p <主进程PID> | wc -l查看实时打开的文件数量,或直接看详细列表:
lsof -p <主进程PID>重点关注
TYPE=PIPE的条目(Queue的管道)和未关闭的普通文件。排查业务函数的文件操作
检查expandDates中的文件操作,确保所有文件都用with语句自动关闭:# 错误示例:未关闭文件 f = open("data.txt", "r") content = f.read() # 正确示例:自动关闭 with open("data.txt", "r") as f: content = f.read()
代码改进方案
最根本的解决方式是用Pool(进程池)替代手动创建Process,它会自动复用进程、控制并发数,避免大量进程同时占用资源:
import os import pandas as pd from multiprocessing import Pool def expandDates(row): # 这里实现你的行扩展逻辑,返回扩展后的DataFrame或列表 # 示例:生成84行数据 expd = pd.DataFrame({"col": [row.col]*84, "date": pd.date_range(row.start_date, periods=84)}) return expd if __name__ == "__main__": # 加载原始数据 thisData = pd.read_csv("your_data.csv") # 设置进程池大小:建议用CPU核心数的1-2倍,避免过度并发 pool_size = os.cpu_count() * 2 # 使用进程池处理,自动管理进程和结果 with Pool(pool_size) as pool: # imap_unordered 支持边处理边获取结果,减少内存占用 results = pool.imap_unordered(expandDates, thisData.itertuples()) # 合并所有扩展后的结果 final_df = pd.concat(results, ignore_index=True) # 保存最终结果 final_df.to_csv("expanded_data.csv", index=False)
改进点说明
- 进程池会复用固定数量的进程,不会一次性创建数百个,大幅降低文件描述符占用。
- 无需手动管理
Queue和进程的start/join/close,减少资源泄漏风险。 imap_unordered比map更高效,适合处理大规模数据,避免一次性加载所有结果到内存。
内容的提问来源于stack exchange,提问作者Dr. Andrew

