如何在Python多进程中避免父进程等待子进程?

5
我对Python的多进程编程还比较陌生,我正在尝试编写一个类,能够异步执行函数并附加回调函数。
首先,让我们为这个特定问题解决一个常见的命名问题:
┬─ process1 (parent)
└─┬─ process2 (child of parent1)
  └─── process3 (child of parent2)

在帮我了解一下这个主题后,我按照这个SO问题的建议,编写了如下代码以实现目标:run方法。
import multiprocessing


class AsyncProcess:
    def __init__(self, target, callback=None, args=(), kwargs={}):
        self._target = target
        self._callback = callback
        self._args = args
        self._kwargs = kwargs
        self._process = None

    def run(self):
        def wrapper():
            return_value = self._target(*self._args, **self._kwargs)

            if self._callback is not None:
                process = multiprocessing.Process(target=self._callback, args=(return_value,))
                process.start()
                multiprocessing.process._children.discard(process)

        self._process = multiprocessing.Process(target=wrapper)
        self._process.start()

AsyncProcess 类比这个更大(它旨在作为 multiprocessing.Processsubprocess.Popen 之间的适配器,用于在新进程中执行外部进程和 Python 函数);这就是为什么它不是 multiprocessing.Process 的子类,而只是在使用它(如果有人想知道的话)。

我想要实现的目标是能够从另一个进程(process2)中启动一个子进程(process3),而不必等待它(process2)(因为子进程可能需要比父进程更长的时间才能完成)。multiprocessing.Processdaemon 属性并没有用,因为当父进程(process2)死亡时,子进程(process3)也会被杀死(而我只想让它一直运行直到完成)。

然而,有两件事情我都不喜欢:

  1. 我正在玩弄 multiprocessing 的内部结构,这一点我一点也不喜欢。
  2. 由于我将子进程(process3)从父进程(process2)的子进程池中删除,我猜这会让可怜的孩子成为孤儿(我不知道确切的含义,但肯定不是好的做法)。

问题在于如何实现父进程不等待子进程而又不杀死子进程或陷入孤儿进程?有没有其他更正确或更优雅的方法来实现我想要做的事情?

我正在考虑将子进程(process3)分配给产生该子进程的进程的父进程(process1),即子进程的曾祖父进程(我知道它肯定还活着),但我还没有找到实现这一点的方法。


一些澄清

  1. Popen可以实现我想要的目标,但它只能处理外部进程,也就是说我不能使用Popen来执行带有所有上下文的Python函数(当然,这是我所知道的)。
  2. 使用os.fork已经考虑过了,但我觉得区分父进程和子进程的方式有点麻烦(处理PID == 0PID != 0的情况等)。
  3. 我没有考虑使用threading包的任何解决方案,因为我想管理进程而不是线程,并将线程管理留给操作系统。
  4. 直接从process1启动process3解决了孤立进程的问题,但我必须对process1进行主动轮询,以便知道process2何时完成,这实际上不是一个选项(process1管理一个不能被阻塞的服务器)。
  5. 我需要尽快让process2完成以获取一些数据;这就是为什么我不直接在process2中执行process3内容的原因。

在撰写问题时我想到的解决方案

由于我的问题是必须从process2内部启动process3,而从process1启动可以解决问题,但在process1上进行主动轮询不可行,因此我也可以同时从process1内部启动process2process3,将process2对象传递给process3,并使用小间隔对process3进行主动轮询,以确保在process2完成后快速响应。

这个方法是否有意义,或者是对已经解决的问题进行了过度复杂的解决方案(而我不知道)?


虽然我不太明白为什么你需要两个子进程,但从理论上讲:使用生产者/消费者模式(和队列)来分离进程1和其他进程之间的依赖关系怎么样?要通知父进程,可以利用发布-订阅模式。 - Maurice Meyer
@MauriceMeyer 我需要它们,因为process1除了生成新进程之外无法执行任何其他操作,process2需要尽早结束(因为我需要进程的退出代码尽快可用于其他目的),而process3则用于执行process2的适当回调函数。也许我对这个问题的整体思考方式不正确;我会查看您的建议,看看它们是否符合我的目的。 - Alejandro de Haro
请问您从更高的角度来看,您想要实现什么目标?只有在确实存在非常特定的用例时,嵌套子进程才不是一个好主意。 - noxdafox
@noxdafox 我需要确切的以下内容:
  1. 主进程需要异步执行一些工作。
  2. 为了处理异步工作的结果,我需要一个回调函数在异步工作之后立即执行,但是与异步工作分离,因为我需要异步工作尽快结束以收集其退出代码。
现在我想起来了,也许我试图太快捕获子进程的退出代码了。只需在同一进程中运行回调并将代码作为参数传递应该足够清晰。
- Alejandro de Haro
1个回答

2
忽略是否好的想法,如果不是通过对内部进行调整而是使用多进程库来做你想要的事情,那是不可能的,这恰恰是因为多进程库是专门为不超过父进程生命周期的子进程设计的。特别设计 我认为答案确实是使用subprocess.Popen(),虽然这意味着放弃多进程库提供的漂亮的高级API。不,你不能直接执行Python函数,但你可以创建一个单独的script.py来调用你想要的函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接