python对文件进行并行计算初探
最近工作中经常会有读取一个文件,对数据做相关处理并写入到另外一个文件的需求
当文件行数较少的时候,单进程顺序读取是没问题的,但是当文件行数过万,就需要消耗很客观的时间。
一、一次性读入,多进程处理
我最初想到的办法是多进程,最初的办法是一次性读取所有行,然后分配给多个进程处理,最终还是写入一个文件。其中需要借助Queue来实现对异常的捕获和处理,不具有可扩展性。
同时一次性读取一个文件,写入内存也受到内存大小的限制。而且这种多进程情况下返回值的处理也比较麻烦。
二、多次读入,并行处理
考虑到Linux有一个按行分割文件的功能split,可以借助她实现数据并行计算,思路是这样的,通过计算文件的总行数,将文件分割成行数相等的多个小文件,小文件个数可以大于或等于并发度。
开启多进程对每个小文件分别处理,每个小文件处理完都输出到一一对应的目标小文件,最终将目标小文件进行合并。
代码如下:
from multiprocessing import Pool import json from time import sleep import requests import os SRC_MID = '_src_' DST_MID = '_dst_' # 业务逻辑处理 def get_jw(addr_name): url = 'https://restapi.amap.com/v3/geocode/geo?address={addr_name}&output=JSON&key=2f114ef951411f01c24a6384b59307a8' result = requests.get(url.format(addr_name=addr_name)) result_str = str(result.content, encoding="utf-8") rj = json.loads(result_str) if len(rj['geocodes']) > 0: jwd = rj['geocodes'][0]['location'] print(jwd) return addr_name + ',' + jwd + '\n' else: print('-,-') return addr_name + ',' + '-,-' + '\n' # 输入源文件,返回分割后的源中间文件list class ParallelCompute(object): def __init__(self, exe_func, source_file, target_file, concurrency=8): self.exe_func = exe_func self.source_file = source_file self.target_file = target_file self.concurrency = concurrency self.abs_src_mid_dir = None self.abs_dst_mid_dir = None self.src_mid_file_list = None self.dst_mid_file_list = None # 源文件分割成多个小文件 def split_file(self): cur_path = os.path.abspath('.') self.abs_src_mid_dir = os.path.join(cur_path, SRC_MID) self.abs_dst_mid_dir = os.path.join(cur_path, DST_MID) os.mkdir(self.abs_src_mid_dir) os.mkdir(self.abs_dst_mid_dir) split_cmd = "split {src_file} {abs_src_mid}/".format(src_file=self.source_file, abs_src_mid=self.abs_src_mid_dir) print(split_cmd) os.system(split_cmd) self.src_mid_file_list = [os.path.join(self.abs_src_mid_dir, it) for it in os.listdir(self.abs_src_mid_dir)] self.dst_mid_file_list = [src_file.replace(SRC_MID, DST_MID) for src_file in self.src_mid_file_list] # 小文件处理 def translate_file(self, src_file, dst_file): with open(src_file, 'rb') as f1, open(dst_file, 'a') as f2: line = f1.readline().strip() line = str(line, encoding='utf8') while line: try: jw = self.exe_func(line) f2.write(jw) except Exception: sleep(5) offset = len(line.encode('utf8')) + 1 f1.seek(-offset, 1) line = f1.readline().strip() line = str(line, encoding='utf8') # 小文件合并 def merge_files(self): with open(self.target_file, 'a') as f2: for dst_m_file in self.dst_mid_file_list: with open(dst_m_file, 'r') as f1: line = f1.readline() while line: f2.write(line) line = f1.readline() # 清理中间文件 def delete_mid_dir(self): os.system('rm -rf %s' % self.abs_src_mid_dir) os.system('rm -rf %s' % self.abs_dst_mid_dir) def execute(self): p = Pool(self.concurrency) self.split_file() for src_mid_file in self.src_mid_file_list: dst_mid_file = src_mid_file.replace(SRC_MID, DST_MID) p.apply_async(self.translate_file, args=(src_mid_file, dst_mid_file,)) p.close() p.join() self.merge_files() self.delete_mid_dir() if __name__ == '__main__': source_file = '/opt/test/qiuxue/target.txt' target_file = '/opt/test/qiuxue/result3.txt' pc = ParallelCompute(get_jw, source_file, target_file) pc.execute()
这样就做到了并行计算和业务逻辑的分离,简化了调用者的使用难度
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗