Python 2中multiprocessing map如何不耗尽生成器实现并行?
当然可行!这事儿在Python 2里完全能实现
你的问题核心在于pool.map()的特性——它会一次性把整个可迭代对象(也就是你的生成器)转换成列表,所以才会先下载完所有文件再启动并行处理。要实现延迟求值、边下载边处理的效果,Python 2的multiprocessing.Pool其实提供了现成的解决方案。
为什么原代码会先耗尽生成器?
pool.map(func, iterable)的内部逻辑是先把iterable完全遍历并转换成列表,再把列表里的元素分配给进程池处理。所以你的downloader.get_files(self.date)生成器会被一次性耗尽,所有文件都下载完后才开始并行解析加载。
解决方案:用imap()或imap_unordered()
这两个方法都是惰性处理可迭代对象的,不会一次性把生成器转成列表,而是取一个元素就交给进程池处理一个,完美符合你“下载几个就并行处理几个”的需求。
代码修改示例
把原来的pool.map()换成pool.imap()即可:
def parse_and_load(filename): raw_records, exchange_data = parser.parse(filename) loader.load(raw_records, exchange_data) with closing(mp.Pool(4)) as pool: # 用imap替代map,实现惰性求值 for _ in pool.imap(parse_and_load, downloader.get_files(self.date)): # 这里不需要处理返回值,只是迭代触发执行流程 pass
两个方法的区别
imap():会保持结果的顺序和输入顺序一致,适合需要按原顺序追踪处理结果的场景。imap_unordered():不保证结果顺序,哪个进程先处理完就先返回哪个,效率可能更高(因为不用等待前面的进程),如果你的业务不关心处理顺序,可以优先用这个。
额外注意事项
- 确保
downloader.get_files(self.date)生成器在主进程中是安全的,生成的filename对应的文件已经下载到本地,且子进程有权限读取。 - 如果生成器生成文件的速度远快于进程池处理速度,还是会先缓存一定数量的本地文件,但至少不会一次性下载全部,能缓解内存/磁盘压力。
- Python 2的
multiprocessing在Windows系统下有序列化限制,确保你的parse_and_load函数和相关对象可以被pickle正常序列化(通常纯函数和简单对象都没问题)。
内容的提问来源于stack exchange,提问作者ayushgp
相关产品推荐
相关产品推荐

