You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python多进程冗余打印问题排查与解决求助

Python multiprocessing进程池日志重复打印问题分析与解决

根因分析

  • 当采用spawn进程启动方式时,进程池会复制主进程的日志配置。如果主进程的日志Handler(比如默认的StreamHandler)没有设置锁,多进程同时向同一输出流(如控制台)写入日志时,会出现IO竞争导致的输出内容重复/重叠。
  • 进程数设为2时,刚好触发两个进程的日志输出竞争冲突;设为3个进程时,只是因任务调度、执行时机的差异,竞争现象未显现(并非不存在,只是概率降低)。
  • 你提到XYZ报错仅出现一次,说明业务逻辑未重复执行,问题仅出在日志IO的并发竞争层面。

解决方法

1. 给日志Handler添加锁

修改日志配置,为输出流Handler显式添加锁,确保同一时间只有一个进程能写入日志:

import logging
import multiprocessing

def setup_logging():
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    
    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter('%(processName)s - %(message)s'))
    handler.createLock()  # 显式创建锁,解决多进程日志竞争
    logger.addHandler(handler)

2. 使用QueueHandler+QueueListener实现进程安全日志

这是更可靠的多进程日志方案:主进程启动QueueListener消费日志队列,子进程仅向队列写入日志,彻底避免直接IO竞争:

import logging
from logging.handlers import QueueHandler, QueueListener
import multiprocessing

def worker_func():
    logger = logging.getLogger()
    logger.info("name: XYZ; value: 12")
    # 业务逻辑代码...

def main():
    log_queue = multiprocessing.Queue(-1)
    handler = logging.StreamHandler()
    formatter = logging.Formatter('%(processName)s - %(message)s')
    handler.setFormatter(formatter)
    listener = QueueListener(log_queue, handler)
    listener.start()

    # 配置主进程日志处理器为QueueHandler
    root_logger = logging.getLogger()
    root_logger.addHandler(QueueHandler(log_queue))
    root_logger.setLevel(logging.INFO)

    # 启动2进程的进程池
    with multiprocessing.Pool(2) as pool:
        pool.apply_async(worker_func)
    
    listener.stop()

if __name__ == "__main__":
    main()

3. 子进程独立初始化日志

在子进程的任务函数开头重新初始化日志,清除继承自主进程的Handler,避免共享IO资源:

def worker_func():
    # 子进程内独立配置日志
    logger = logging.getLogger()
    logger.handlers.clear()  # 清除主进程继承的Handler
    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter('%(processName)s - %(message)s'))
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)
    
    logger.info("name: XYZ; value: 12")

内容的提问来源于stack exchange,提问作者soumeng78

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 12:22:10