Python多进程join()未等待target_function执行完成的原因排查
问题描述
在多进程实验中,使用multiprocessing.Process创建守护进程,target_function会随机休眠5-10秒后输出内容并向队列存值。多次调用t1函数后出现以下现象:
- 进程
is_alive()返回False、exit_code为0,但join()并未等待target_function的print/日志输出完成 - 部分进程的
target_function输出滞后到后续进程启动后才出现
复现代码
import multiprocessing import random, time import logging from logging.handlers import RotatingFileHandler logging.basicConfig( handlers=[RotatingFileHandler('log.txt')],level=logging.INFO) logger = logging.getLogger(__name__) TIMEOUT = 5 PID = None def target_function(queue_1): sleep_value = random.choice(range(5,10)) time.sleep(sleep_value) print('target_function', sleep_value) logger.info(f'target_function {sleep_value}') queue_1.put(True) def t1(): print('###############') logger.info('###############') queue_1 = multiprocessing.Queue() queue_value = False process = multiprocessing.Process(target=target_function, args=(queue_1, )) process.daemon = True process.start() PID = process.pid time.sleep(TIMEOUT) if not queue_1.empty(): queue_value = queue_1.get(timeout=3) print(PID, queue_value) logger.info(f'PID: {queue_value}') queue_1.close() process.join() print(PID, process.is_alive(), process.exitcode) logger.info(f'PID: {process.is_alive()}, {process.exitcode}') del process t1() t1()
原因分析
1. 标准输出(stdout)的缓冲延迟
Python的print函数默认采用行缓冲机制,但在多进程环境下,子进程的stdout缓冲区不会实时刷新到终端。只有当子进程终止、缓冲区满,或者主动调用刷新操作时,缓冲内容才会输出。当你调用process.join()时,会等待子进程执行完毕,子进程终止时才会触发stdout缓冲区的刷新,而此时主进程可能已经进入下一次t1调用,导致子进程的输出看起来滞后到后续进程启动之后。
2. 日志处理器的进程不安全问题
你使用的RotatingFileHandler并非进程安全的日志组件。多个子进程同时向同一个日志文件写入内容时,会存在资源竞争,导致日志内容的写入顺序错乱、延迟甚至部分丢失。这也是日志输出滞后的重要原因之一。
3. 执行时序的不匹配
target_function的休眠时间为5-10秒,而t1函数中固定休眠5秒后才执行后续逻辑。当target_function的休眠时间超过5秒时,子进程在t1执行process.join()时仍在运行,join()会等待子进程结束,但子进程的输出缓冲要到进程终止时才会刷新,这就导致输出在join()之后才出现,而此时主进程可能已经启动了下一个t1的进程,造成输出滞后的错觉。
补充解决建议
- 强制刷新stdout:在
print语句中添加flush=True参数,比如print('target_function', sleep_value, flush=True),确保输出实时刷新。 - 使用进程安全的日志方案:采用
logging.handlers.QueueHandler+QueueListener的组合,让主进程统一处理日志写入,避免多进程直接操作日志文件。 - 优化等待逻辑:不要依赖固定时长的
time.sleep,而是通过队列的get方法(不带超时或合理超时)等待子进程的结果,或者使用multiprocessing.Event来同步子进程的执行状态。
内容的提问来源于stack exchange,提问作者vishak raj
相关产品推荐
相关产品推荐

