繁体   English   中英

python 多处理子进程未正常退出

[英]python multiprocessing child processes not quiting normally

我一直在使用 python 多处理来处理一些任务。 开发环境是 Windows Server 2016 和 python 3.7.0。
有时会有子进程留在任务列表中。 但实际上,它们似乎已完成(数据已写入数据库)。 影响是日志卡在那里,无法 append 最新日志。

进程不退出

这是代码。 主要 function 启动一个监听进程和几个工作进程:

queue = multiprocessing.Queue(-1)
listener = multiprocessing.Process(target=listener_process, args=(queue, listener_configurer))
listener.start()

...

workers = []
for loop:
    worker = multiprocessing.Process(target=process_start, args=(queue, worker_configurer, plist))
    workers.append(worker)
    worker.start()
for w in workers:
    w.join()

...

queue.put_nowait(None)
listener.join()

侦听器进程在它得到 None 时结束,从而导致整个任务结束。

def listener_process(queue, configurer):
    configurer()
    while True:
        try:
            record = queue.get()
            if record is None:
                break
            if type(record) is not int:
                Logger = logging.getLogger(record.name)
                Logger.handle(record)
        except Exception as e:
            Logger.error(str(e), exc_info=True)

任务由 windows 任务调度程序调度运行。
知道为什么一些多处理进程“卡”在那里吗?
它困扰了我一段时间。 提前致谢。

我可以肯定地说你的问题是什么吗? 不,我可以肯定地说你正在做一些可能导致僵局的事情吗? 是的。

如果您仔细阅读有关multiprocessing.Queue的文档,您将看到以下警告:

警告:如上所述,如果子进程已将项目放入队列(并且它没有使用JoinableQueue.cancel_join_thread ),则该进程将不会终止,直到所有缓冲项目都已刷新到 pipe。

这意味着如果您尝试加入该进程,您可能会遇到死锁,除非您确定已放入队列的所有项目都已被消耗。 类似地,如果子进程是非守护进程,则父进程在尝试加入其所有非守护子进程时可能会挂起退出。

请注意,使用管理器创建的队列不存在此问题。 请参阅编程指南。

这意味着为了完全安全,您必须先加入侦听器进程(从队列中发出 get),然后再加入workers进程(向队列发出 put),以确保放入队列的所有消息都已在您尝试加入已完成放入队列的任务之前,请先读取队列。

但是,如果当前正在寻找主进程向队列写入None标记消息,表示它正在退出时间,那么侦听器进程将如何知道何时终止,但在新设计中,主进程必须首先等待侦听器在等待工人终止之前终止? 大概您可以控制process_start function 的源,它实现了写入队列的消息的生产者,并且可能触发了它终止的决定。 当这些进程终止时,它们必须各自向队列写入一条None标记消息,表示它们将不再产生任何消息。 然后函数listener_process必须传递一个附加参数,即消息生产者的数量,以便它知道它应该期望看到多少这些哨兵 不幸的是,我无法从您编码的内容(即for loop: )中辨别出该进程的数量是多少,并且您似乎正在使用相同的 arguments 实例化每个进程。 但为了清楚起见,我会将您的代码修改为更明确的内容:

queue = multiprocessing.Queue(-1)
listener = multiprocessing.Process(target=listener_process, args=(queue, listener_configurer, len(plist)))
listener.start()

...

workers = []
# There will be len(plist) producer of messages:
for p in plist:
    worker = multiprocessing.Process(target=process_start, args=(queue, worker_configurer, p))
    workers.append(worker)
    worker.start()
listener.join() # join the listener first
for w in workers:
    w.join()


....


def listener_process(queue, configurer, n_producers):
    configurer()
    sentinel_count = 0
    while True:
        try:
            record = queue.get()
            if record is None:
                sentinel_count += 1
                if sentinel_count == n_producers:
                    break # we are done
                continue
            if type(record) is not int:
                Logger = logging.getLogger(record.name)
                Logger.handle(record)
        except Exception as e:
            Logger.error(str(e), exc_info=True)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM