如何为列表中每个对象生成新进程,替代串行执行以提速?
用Python多进程优化串行遍历的耗时问题
嗨,针对你这个串行处理80个对象耗时7小时的问题,换成多进程并行处理绝对是最优解之一!下面我给你具体的实现方案、示例代码,以及一些实操中的注意事项,帮你把运行时间大幅压缩。
推荐工具:Python标准库multiprocessing
Python的multiprocessing模块是专门用来实现多进程并行的,相比subprocess(更适合调用外部程序),它更适合Python内部函数的并行执行,轻量且进程间通信更方便。而且它能绕过GIL(全局解释器锁)的限制,真正利用多核CPU的性能,完美解决你的串行耗时问题。
两种常用的多进程实现方式
方式1:进程池(Pool)—— 最简洁的批量处理方案
进程池会帮你管理进程的创建、复用和销毁,不用手动维护每个进程,非常适合处理批量对象的场景。
示例代码
假设你原来的串行代码是这样的:
listName = [obj1, obj2, ..., obj80] i = 0 while i < len(listName): print(f"blah {i}") # 这里是你对每个对象的多项操作,比如: # listName[i].task1() # listName[i].task2() i += 1
改成进程池版本:
import multiprocessing # 把每个对象的操作封装成单独的函数 def process_single_object(obj, index): print(f"blah {index}") # 这里放你原来的多项操作逻辑 obj.task1() obj.task2() # 如果有返回结果,直接return即可,进程池会收集结果 if __name__ == "__main__": listName = [obj1, obj2, ..., obj80] # 获取CPU核心数(也可以手动指定,比如8,不用超过核心数太多) worker_count = multiprocessing.cpu_count() # 创建进程池并执行任务 with multiprocessing.Pool(worker_count) as pool: # 用starmap传递多个参数(对象和索引) results = pool.starmap( process_single_object, [(obj, idx) for idx, obj in enumerate(listName)] ) # 如果有返回结果,results就是所有进程的返回值列表 # print(results)
方式2:手动创建Process对象—— 更精细的进程控制
如果你需要对每个进程的启动、关闭、资源分配做更精细的控制,可以手动创建Process对象:
import multiprocessing def process_single_object(obj, index): print(f"blah {index}") # 你的业务操作逻辑 if __name__ == "__main__": listName = [obj1, obj2, ..., obj80] processes = [] # 逐个创建并启动进程 for idx, obj in enumerate(listName): p = multiprocessing.Process( target=process_single_object, args=(obj, idx) ) processes.append(p) p.start() # 等待所有进程执行完成 for p in processes: p.join()
实操中的关键注意事项
- 必须加
if __name__ == "__main__":Windows系统下多进程需要这个判断来避免重复启动子进程,跨平台开发时一定要加上,否则会报错。 - 对象可序列化:传递给子进程的对象必须是可
pickle序列化的,如果你的自定义对象不能被序列化,建议传递对象的关键参数(比如ID、路径),在子进程里重新初始化对象。 - 进程数不要贪多:进程数设置为CPU核心数的1-2倍即可,太多会导致进程切换开销剧增,反而变慢。比如8核CPU,设8-12个进程就好。
- 日志与输出:多进程下
print的内容会乱序,建议用logging模块替代,给每个日志加上进程ID或对象索引,方便排查问题。 - 错误处理:如果某个对象处理失败,最好在
process_single_object里用try-except捕获异常,或者用进程池的error_callback回调处理错误:def handle_error(exc): print(f"处理对象时出错:{exc}") with multiprocessing.Pool(worker_count) as pool: for idx, obj in enumerate(listName): pool.apply_async( process_single_object, args=(obj, idx), error_callback=handle_error ) pool.close() pool.join() - 关于
subprocess:如果你每个对象的操作是调用外部脚本/命令,subprocess也可以用,但需要手动管理子进程的启动和等待,复杂度比multiprocessing高。如果是Python内部逻辑,优先用multiprocessing。
效果预期
如果你的任务是CPU密集型,用8核CPU的话,理论上能把7小时的耗时压缩到1小时左右(实际取决于任务的并行度);如果是IO密集型(比如等待网络、文件读写),提升效果会更明显。
内容的提问来源于stack exchange,提问作者Kimomaru
相关产品推荐
相关产品推荐

