Python借助subprocess并行执行Linux命令时多DOCX转PDF任务异常
问题根因
你的并行转换失效完全是LibreOffice soffice 的内置实例互斥机制导致的,和Python的线程池逻辑没有关系:
- 两个转换任务几乎同时触发
soffice命令时,默认都会读写同一个全局用户配置目录(Linux下路径为~/.config/libreoffice/4/user,Windows下为%APPDATA%\LibreOffice\4\user)。 - soffice 自带锁机制:新进程启动时如果检测到同配置目录已有运行实例,会直接把自身的转换请求转发给已有进程,随后立刻退出,不会初始化独立的转换环境。
- 两个任务同时抢锁时,后启动的进程会接管全部流程,先启动的进程因为拿不到配置目录的独占访问权,转换流程直接中断;且你没有打印子进程的报错信息,完全感知不到这个失败,最后就只剩后触发的1.docx完成了转换,生成了1.pdf。
解决方法
核心思路是给每个独立运行的soffice进程分配专属的临时用户配置目录,绕开内置的实例互斥锁,就可以真正实现并行转换。
具体改造点就是在soffice的启动参数中新增指向独立临时目录的-env:UserInstallation配置,改造后的可运行代码如下:
from multiprocessing.dummy import Pool import subprocess import tempfile import os def convert_to(source): # 为每个转换任务生成唯一的临时LibreOffice配置目录,从根源避免互斥冲突 profile_path = tempfile.mkdtemp(prefix=f"lo_profile_{os.path.basename(source)}_") args = [ "soffice", "--headless", "--nologo", "--norestore", "--convert-to", "pdf:writer_pdf_Export", # 新增独立配置目录参数,路径格式匹配soffice要求的file://前缀格式 f"-env:UserInstallation=file://{profile_path}", source, ] process = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, ) stdout, stderr = process.communicate() # 补充非0退出码的错误打印,方便后续排查异常 if process.returncode != 0: print(f"文件{source}转换失败,错误信息:{stderr.decode('utf-8', errors='ignore')}") return stdout, stderr # 可根据机器配置调整并发数,一般2-4个并发就能跑满磁盘IO pool = Pool(2) print(pool.map(convert_to, ["0.docx", "1.docx"]))
注意事项
- 网上常见的给soffice加
--nologo--norestore参数只能减少启动耗时,完全解决不了同配置目录抢锁的问题,独立的UserInstallation路径是必须的配置。 - 转换任务执行完成后,临时配置目录会自动留在系统临时文件夹中,可定期清理,单个目录仅占数MB空间,不会有太大磁盘占用。
- 后续如果要进一步提升转换效率,完全可以把线程池替换成多进程池,只要保持每个soffice实例对应独立的配置目录,就不会再出现任务互斥的问题。
内容的提问来源于stack exchange,提问作者user_12
相关产品推荐
相关产品推荐

