【问题标题】:Why child process (daemon=True) not exiting when main process exit in python?为什么在python中主进程退出时子进程(daemon = True)不退出?
【发布时间】:2018-09-11 07:11:33
【问题描述】:

这里是python多处理中daemon标志的官方解释:

当一个进程退出时,它会尝试终止其所有守护进程。

据我了解,父进程在退出时会杀死其守护进程标志设置为 True 的子进程。

下面是我用来证明我的猜测的代码。但结果不一样。

import multiprocessing


def child():
    while True:
        pass


for x in xrange(1, 4):
    proc = multiprocessing.Process(target=child, args=())
    proc.daemon=True
    proc.start()


while True:
    pass

上面启动了4个子进程和1个主进程。 我杀死了主进程,但 4 个孩子没有退出。

既然守护进程设置为 true,为什么它们没有被 main 终止?

【问题讨论】:

  • 您使用的是哪个操作系统?当我在 Windows 上尝试时,我得到了一堆与进程相关的错误。
  • 我在我的 Linux 系统上运行了这个并添加了一个答案,检查一下。

标签: python process daemon


【解决方案1】:

注意事项

  • xrange的使用暗示Python 2
  • xrange(1, 4) 将产生 3 个值而不是 4 个(因此,只有 3 个孩子)

事情并非如此。文档 ([Python 2.Docs]: multiprocessing - daemon) 可能应该更具体。

问题是 multiprocessing 注册了一个 cleanup 函数 以在退出时杀死其所有 deamonic 子级。这是通过[Python 2.Docs]: atexit - Exit handlers 完成的:

注意:当程序被 Python 未处理的信号杀死、检测到 Python 致命内部错误或调用 os._exit() 时,不会调用通过此模块注册的函数.

您不处理 TERM 信号(默认由 kill 命令发送),因此主进程不会调用清理函数(保留其子进程)运行)。

我修改了您的代码以更好地说明行为。

code00.py

#!/usr/bin/env python2

import sys
import multiprocessing
import os
import time


print_text_pattern = "Output from process {0:s} - pid: {1:d}, ppid: {2:d}"


def child(name):
    while True:
        print(print_text_pattern.format(name, os.getpid(), os.getppid()))
        time.sleep(1)


def main():
    procs = list()
    for x in xrange(1, 3):
        proc_name = "Child{0:d}".format(x)
        proc = multiprocessing.Process(target=child, args=(proc_name,))
        proc.daemon = True #x % 2 == 0
        print("Process {0:s} daemon: {1:}".format(proc_name, proc.daemon))
        procs.append(proc)

    for proc in procs:
        proc.start()

    counter = 0
    while counter < 3:
        print(print_text_pattern.format("Main", os.getpid(), os.getppid()))
        time.sleep(1)
        counter += 1


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

注意事项

  • 稍微改变了子进程的生成方式:所有子进程都是在 1st 创建的,然后才开始
  • 添加了来自每个进程的一些 print 调用,以跟踪它们在 stdout 中的活动 - 还添加了一些 time.sleep 调用(1 秒),以避免产生过多输出
  • 最重要的 - 主进程不再永远运行。在某些时候,它会优雅地退出(在 3 个周期后 - 由于 counter 变量),并且我之前提到的行为会出现。
    这也可以通过拦截 TERM 信号(以及其他可以通过 kill 命令显式发送的信号)并执行清理 - 这样子进程也会在杀死主进程时被杀死 -但这更复杂
  • 我稍微简化了一下,只生成了 2 个孩子
  • 移动了 main 函数(用于结构)中的所有内容,该函数包含在 if __name__ == "__main__": 条件中,因此如果您导入模块
  • ,则不会生成进程
  • 为每个孩子指定不同的值proc.daemon,然后监控输出和ps -ef | grep "code00.py" 输出
  • child func 添加了一个参数 (name),但这仅用于显示目的

输出

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow]> python2 code00.py
Python 2.7.12 (default, Oct  8 2019, 14:14:10) [GCC 5.4.0 20160609] 64bit on linux2

Process Child1 daemon: True
Process Child2 daemon: True
Output from process Main - pid: 1433, ppid: 1209
Output from process Child1 - pid: 1434, ppid: 1433
Output from process Child2 - pid: 1435, ppid: 1433
Output from process Main - pid: 1433, ppid: 1209
Output from process Child2 - pid: 1435, ppid: 1433
Output from process Child1 - pid: 1434, ppid: 1433
Output from process Main - pid: 1433, ppid: 1209
Output from process Child1 - pid: 1434, ppid: 1433
Output from process Child2 - pid: 1435, ppid: 1433
Output from process Child1 - pid: 1434, ppid: 1433
Output from process Child2 - pid: 1435, ppid: 1433

Done.

【讨论】:

  • 解释得很好!接受我的支持:) 简而言之,你的意思是说,当他们让主进程无限期地运行直到他们突然杀死它时,OP 没有处理 TERM 信号?这就是为什么不调用子进程的清理函数的原因?
  • @nj2237:谢谢。是的,如果 OP 将处理 TERM,并且在处理程序内部将调用 multiprocessing 注册的清理函数,则问题中的代码将按预期运行。
  • 好的,感谢您的澄清。所以在主进程中调用sys.exit() 也会处理TERM,这就是它对我有用的原因。
  • @nj2237 我不这么认为。 sys.exit 优雅地退出进程,并执行 atexit 函数。我不确定它是否发送信号(没有深入研究代码)。
  • 好的,我会编辑我的答案。我也检查了这个 - pymotw.com/2/atexit/index.html 并且看起来调用 sys.exit 也会注册 atexit 回调。
【解决方案2】:

是的,您的理解是正确的,您的测试代码也有效。

我只是添加了一些 sleep 语句来调试输出(没有sleep,很难从打印的巨大输出中推断出来):

import multiprocessing
import time
import sys

print("main")

def child():
    while True:
        print("child")
        time.sleep(3)

for x in xrange(1, 4):
    proc = multiprocessing.Process(target=child, args=())
    proc.daemon=True
    proc.start()

time.sleep(7)
print("exit")
sys.exit() # this exits the main process

现在,当我运行这个脚本时,当它运行时,我做了一个ps aux 并且可以看到从这个脚本运行的四个进程。 7 秒后,当我再次执行 ps aux 时,我再也看不到那些正在运行的进程 - 这意味着:

当主进程退出时,它终止了它所有的守护子进程。

之后,我还将proc.daemon 设置为False,并再次运行脚本。这一次,即使在 7 秒后,当我执行 ps aux 时,我仍然可以看到子进程正在运行(因为它们现在是非守护进程,即使在主进程终止后它们也不会退出)。

所以这按预期工作 - 如果您在这里仍有问题,请告诉我。

编辑 1: 感谢@CristiFati 指出最初的清理问题。 此代码有效,因为调用 sys.exit() 还会将 atexit 回调注册为详细说明的 here

【讨论】:

    猜你喜欢
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 2017-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多