Python multiprocessing.Queue 出现死锁问题求助
Python multiprocessing.Queue 出现死锁问题求助
看起来你的问题大概率是子进程在完成文件读取后,后续代码触发了未被捕获的异常,导致子进程直接退出,根本没执行到results_queue.put()这一步,父进程就一直卡在get()上等着,形成了死锁。结合你看到的日志——子进程打印了“Finished reading”但没打印“Child process returning result”,正好印证了这一点。
我给你分析几个关键点和解决办法:
1. 先排查异常捕获范围的问题
你当前的except Exception as e只能捕获常规的异常,但Python里像KeyboardInterrupt、SystemExit甚至一些底层的信号终止(比如被系统OOM Killer干掉)都不属于Exception的子类,这些情况会直接让子进程崩溃,不会进入你的except块,自然也就不会执行put()。
建议把捕获范围扩大到BaseException,并且加上finally块确认子进程的退出状态:
def _process_pdf_pages(original_file, arg1, arg2, arg3,..., results_queue): try: logger.info('{} Started reading PDF/XPS file {}'.format(dt.datetime.now(), original_file)) # ... 读取文件的代码 logger.info('{} Finished reading PDF/XPS file {}'.format(dt.datetime.now(), original_file)) # --- 这里加更多日志,定位卡在哪一步 --- logger.info('Starting post-processing steps') # ... 你原来的后续处理代码 logger.info('Child process returning result') results_queue.put((arg1, arg2, arg3...)) except BaseException as e: logger.error('Child process encountered FATAL error: {}'.format(e)) logger.error(traceback.format_exc()) logger.info('Child process returning error marker') # 可以在返回结果里加个错误标记,让父进程知道出问题了 results_queue.put((arg1, arg2, arg3, None)) finally: logger.info('Child process is exiting now')
2. 给父进程的get()加超时保护
一直无限等待肯定不是办法,给get()加个超时时间,这样即使子进程挂了,父进程也不会一直卡死,还能检查子进程的退出状态:
import queue def parent_function(): # ... 其他代码 results_queue = multiprocessing.Queue() child_process = multiprocessing.Process(target=_process_pdf_pages, args=(original_file, arg1, arg2, arg3,..., results_queue)) child_process.start() logger.info('BEGIN results = results_queue.get()') try: # 比如设置60秒超时,根据你的处理时间调整 results = results_queue.get(timeout=60) logger.info('END results = results_queue.get()') except queue.Empty: logger.error('Timeout waiting for child process result') # 检查子进程是否还活着 if child_process.is_alive(): logger.warning('Child process is still running, force terminating it') child_process.terminate() child_process.join() else: logger.info(f'Child process exited with code: {child_process.exitcode}') # 退出码是负数的话,说明是被信号终止的(比如SIGKILL) if child_process.exitcode < 0: logger.error(f'Child process was killed by signal: {-child_process.exitcode}') # ... 其他代码
3. 定位子进程卡死的具体位置
在“Finished reading”之后的每一段代码前都加上日志,比如:
logger.info('Starting page analysis') # ... 页面分析代码 logger.info('Page analysis done, starting result formatting') # ... 结果格式化代码
这样下次出现死锁时,你就能通过日志知道子进程到底执行到哪一步停了,精准定位问题代码。
其他可能的小概率原因
- 子进程被系统资源限制干掉了:比如内存不足被OOM Killer终止,这种情况子进程的退出码会是负数(比如-9对应SIGKILL),可以通过
child_process.exitcode查看。 multiprocessing.Queue的缓冲区问题:不过你只put一次结果,缓冲区不可能满,这个可能性极低。
先按上面的方法调整代码,应该能解决你的死锁问题,或者至少能定位到具体原因。
备注:内容来源于stack exchange,提问作者Travis Lu
相关产品推荐
相关产品推荐

