我有几个我正在解析的大型xml文件(提取数据的一些子集并写入文件),但每个文件有很多文件和大量记录,所以我试图并行化。
首先,我有一个从文件中提取记录的生成器(这很好):
def reader(files):
n=0
for fname in files:
chunk = ''
with gzip.open(fname, "r") as f:
for line in f:
line = line.strip()
if '<REC' in line or ('<REC' in chunk and line != '</REC>'):
chunk += line
continue
if line == '</REC>':
chunk += line
n += 1
yield chunk
chunk = ''
一个过程函数(这里的细节不相关,但也可以正常工作):
def process(chunk,fields='all'):
paper = etree.fromstring(chunk)
#
# extract some info from the xml
#
return result # result is a string
现在当然,天真的,非平行的方式就是这样简单:
records = reader(files)
with open(output_filepath,'w') as fout:
for record in records:
result = process(record)
fout.write(result+'\n')
但现在我想并行化这个。我首先考虑做一个简单的基于地图的方法,每个进程处理一个文件,但文件大小完全不同(有些文件非常大),所以这对于并行化的使用效率非常低,我认为。这是我目前的做法:
将多处理导入为mp
def feed(queue, records):
for rec in records:
queue.put(rec)
queue.put(None)
def calc(queueIn, queueOut):
while True:
rec = queueIn.get(block=True)
if rec is None:
queueOut.put('__DONE__')
break
result = process(rec)
queueOut.put(result)
def write(queue, file_handle):
records_logged = 0
while True:
result = queue.get()
if result == '__DONE__':
logger.info("{} --> ALL records logged ({})".format(file_handle.name,records_logged))
break
elif result is not None:
file_handle.write(result+'\n')
file_handle.flush()
records_logged +=1
if records_logged % 1000 == 0:
logger.info("{} --> {} records complete".format(file_handle.name,records_logged))
nThreads = N
records = reader(filelist)
workerQueue = mp.Queue()
writerQueue = mp.Queue()
feedProc = mp.Process(target = feed , args = (workerQueue, records))
calcProc = [mp.Process(target = calc, args = (workerQueue, writerQueue)) for i in range(nThreads)]
writProc = mp.Process(target = write, args = (writerQueue, handle))
feedProc.start()
for p in calcProc:
p.start()
writProc.start()
feedProc.join()
for p in calcProc:
p.join()
writProc.join()
feedProc.terminate()
writProc.terminate()
for p in calcProc:
p.terminate()
workerQueue.close()
writerQueue.close()
现在,这可以理解为所有内容都写入文件,但是当它最终尝试加入进程时它就会挂起,我不知道为什么。所以,我的主要问题是,我在这里做错了什么,以至于我的工作流程没有正确终止,或者表明他们已经完成了?
我认为我可以通过向join
的调用添加超时来解决这个问题,但这个(a)似乎是一个相当不优雅的解决方案,因为任务有明确的完成条件(即一旦文件中的每个记录都被处理完毕,我们就完成了,并且(b)我担心这可能会引入一些问题(例如,如果我将超时设置得太短,就不能在所有事情发生之前终止事情已经处理好了吗?当然太长了只是浪费时间......)。
如果有人有想法,我也愿意考虑一种完全不同的并行化方法(队列看起来是一个很好的选择,因为文件很大而只是阅读和生成原始记录需要时间)。
奖金问题:我知道这种方法绝不能保证我写入文件的输出与原始数据的顺序相同。这不是一个大问题(对减少/处理的数据进行排序不会太笨重),但维护顺序会很好。如果有人有解决方案可以确保保留原始订单,那就更感激了。