Python多进程和子进程的独立性

12

从 Python 终端运行以下命令,生成一个长时间运行的子进程:

from multiprocessing.process import Process
Process(target=LONG_RUNNING_FUNCTION).start()

这个命令会返回,我可以在Python终端中执行其他操作,但是由子进程打印的任何内容仍然会被打印到我的Python终端会话中。

当我退出终端(使用 exitCTRL+D)时,退出命令就会挂起。如果在此挂起期间按下 CTRL+C,则子进程将终止。

如果我手动杀死 Python 终端进程(通过 posix 的 kill 命令),则子进程将变成孤儿,并继续运行,输出可能被丢弃。

如果我使用 python -c 运行此代码,则它会等待子进程终止,CTRL+C 会同时杀死父进程和子进程。

哪些 Python 运行配置会在终止父进程时终止子进程?特别是,如果 Python-mod_wsgi-apache Web 服务器生成子进程,然后重新启动,子进程是否会被终止?

[ 顺便提一句,有没有比以下方法更优雅的方式从终端分离生成的子进程?Deliberately make an orphan process in python ]

更新:在由 Apache 运行的 Web 服务器下使用 multiprocessing.Process 生成的 Python 子进程在 Apache 重新启动时不会被终止。

1个回答

21

这不是关于如何调用Python的问题,而是关于multiprocessing模块的特性。当你导入该模块时,在父进程中添加了一个退出处理程序,它会在允许父进程退出之前对所有通过multiprocessing.Process创建的子进程的Process对象调用join()方法。如果你要以这种方式启动子进程,除非修改模块内部机制,否则无法避免导致麻烦的行为。

如果你想启动一个能够超过父进程寿命的进程,那么最好使用subprocess.Popen。如果子进程是这样启动的,那么父进程在退出之前不会尝试加入子进程,而是立即退出,留下了孤儿子进程:

>>> from subprocess import Popen
>>> Popen(["sleep", "100"], start_new_session=True)
<subprocess.Popen object at 0x10d3fedd0>
>>> exit()
alp:~ $ ps -opid,ppid,command | grep sleep | grep -v grep
37979     1 sleep 100

你使用 multiprocessing 而不是 subprocess 有特殊的原因吗?前者并不适用于创建比父进程更长寿命的子进程;它用于创建可以在多个 CPU 上并行工作的子进程,以规避全局解释器锁定。 (出于本讨论的目的,我忽略了 multiprocessing 的分布式能力。) 因此,通常在那些如果没有全局解释器锁,则会使用线程的情况下使用multiprocessing。(请注意,在这方面,multiprocessing 模块的 API 设计与 threading 模块非常相似。)

针对你帖子末尾的具体问题:(1) Python 中没有任何东西负责在终止父进程时杀死子进程。Web 服务器的子进程只有在父进程退出之前由父进程杀死它(或整个进程组被杀死)才会停止运行。(2) 链接中提到的方法似乎是在没有了解如何标准实现守护进程的惯用语境的情况下尝试复制守护进程。有许多包可用于创建守护进程;你应该使用其中之一。


subprocess是我用于分派系统调用的常用模块,但使用subprocess来启动后台Python任务似乎不太合适。你有没有用过一个好的Python守护进程库(例如python-daemon)? - Zags
2
背景/前景的区分是shell作业控制特有的;在进程管理的一般应用中使用这些术语是没有意义的。在更一般的情况下,守护进程是进程自己执行的操作,并且使用subprocess.Popen启动这样的进程是完全标准的。python-daemon很好;它具有繁琐的API,但经过了实战考验,后者的质量很重要,因为很容易出错。 - Alp

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