如何在openpyxl对象中使用多进程?

时间:2017-04-11 15:39:46

标签: openpyxl

我需要处理一个包含很多工作表的Excel,但是每个工作表都有大数据。如果使用openpyxl加载这个excel,那将花费大量时间,所以我想通过多进程分析每个工作表。 / p>

这样的简短代码:

import multiprocessing as mp
import openpyxl
def LoadEx():
        wb=openpyxl.load_workbook('example.xlsx')
        sheetnames=wb.get_sheet_names()
        return sheetnames, wb

def job(sheet,wb):
    gs=wb.get_sheet_by_name(sheet)
    for i in range(10):
        if gs.cell(row=i,column=2).value=='Target':
            gs.cell(row=i,column=3).value='OK'

if __name__=='__main__':
    sheetnames,wb=LoadEx()
    pool=mp.Pool()
    for sheetname in sheetnames:
        res=pool.apply_async(job, (sheetname,wb))
    pool.close()
    pool.join()
    wb.save('example_output.xlsx')

但是,文件'example_output.xlsx'似乎没有保存job()的结果, 在这种情况下,我该如何获得多进程的效果? 可能有人可以帮助我,思考

2 个答案:

答案 0 :(得分:1)

您可以使用multiprocessing执行此操作,但您必须支付

  1. global wb成为您使用的每个process副本。 因此,使用4 processes,您的内存必须足够大,才能容纳4个工作簿副本。

  2. 鉴于wb是副本,您的更改属于此副本。 您必须将更改复制到一个工作簿中。 复制工作表可能非常耗时。

  3. 为了克服酸洗错误,我从排队ws变为wsDiff。 而不是写入ws副本,将更改聚合到wsDiff。 作为奖励,复制到目标wb会更快。

      

    时间表:cpu_count = 2,10个工作表,工作量:def ws_job(...

    Job Processes   without mp           2               4  
    Time          0:00:21.260746  0:00:10.214942  0:00:07.097369  
    

    此工作示例适用于给定的问题def job。 ,例如:

    import multiprocessing as mp
    import queue, os, time
    import random as rd
    import openpyxl
    
    
    class wsDiff(object):
        def __init__(self, row, column, value):
            self.row = row
            self.column = column
            self.value = value
    
    
    def ws_job(wb, ws_idx):
        ws = wb.worksheets[ws_idx]
        print('pid %s: process (%s)' % (os.getpid(), ws.title))
    
        # *** DO SOME STUFF HERE***
        # Simulate workload
        time.sleep(rd.randrange(1, 4))
    
        diff = []
        for i in range(1, 11):
            if ws.cell(row=i, column=2).value == 'Target':
                #ws.cell(row=i, column=3).value = 'OK'
                diff.append( wsDiff(i, 3, 'OK') )
    
        return diff
    
    
    def job(fq, q, wb):
        while True:
            try:
                ws_idx = fq.get_nowait()
            except queue.Empty:
                print('pid %s: exit job' % os.getpid())
                exit(0)
    
            q.put((ws_job(wb, ws_idx), ws_idx))
            time.sleep(0.1)
    
    
    def writer(q, wb):
        print('start writer_handler')
        while True:
            try:
                diff, i_ws = q.get()
            except ValueError:
                print('writer ValueError exit(1)')
                exit(1)
    
            if diff == None:
                wb.save('../test/example_output.xlsx')
                exit(0)
    
            ws = wb.worksheets[i_ws]
            print('pid %s: update sheet %s from diff' % (os.getpid(), ws.title))
            for d in diff:
                ws.cell(row=d.row, column=d.column).value = d.value
    
    
    def mpRun():
        wb = openpyxl.load_workbook('../test/example.xlsx')
    
        f_q = mp.Queue()
        for i in range(len(wb.worksheets)):
            f_q.put(i)
    
        w_q = mp.Queue()
        w_p = mp.Process(target=writer, args=(w_q, wb))
        w_p.start()
        time.sleep(0.1)
    
        pool = [mp.Process(target=job, args=(f_q, w_q, wb)) for p in range(os.cpu_count() + 2)]
        for p in pool:
            p.start()
            time.sleep(0.1)
    
        for p in pool:
            p.join()
    
        time.sleep(0.2)
        # Terminate Process w_p after all Sheets done
        w_q.put((None, None))
        w_p.join()
    
        print('EXIT __main__')
    

    使用Python测试:3.4.2 - openpyxl:2.4.1 - LibreOffice:4.3.3.2

答案 1 :(得分:0)

不幸的是,Workbooks不适合多处理,因为有很多共享状态。