为什么Python中主进程退出时子进程(daemon=True)不退出?

Kra*_* Li 3 python daemon process

这里是python multiprocessing中flag的官方解释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
Run Code Online (Sandbox Code Playgroud)

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

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

Cri*_*ati 6

注意事项

  • 使用xrange意味着Python 2

  • xrange(1, 4)将产生 3 个值而不是 4 个(因此,只会有 3 个子项)

事情并不完全是这样的。文档([Python 2.Docs]:multiprocessing - daemon)可能应该更具体。

问题是,多处理注册了一个清理函数,以在退出时杀死所有其守护子进程。这是通过[Python 2.Docs] 完成的:atexit - 退出处理程序重点是我的):

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

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

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

代码00.py

#!/usr/bin/env python2

import multiprocessing
import os
import sys
import time


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


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


def main(*argv):
    procs = list()
    for x in xrange(1, 3):
        proc_name = "Child{:d}".format(x)
        proc = multiprocessing.Process(target=child, args=(proc_name,))
        proc.daemon = True #x % 2 == 0
        print("Process {:s} daemon: {:}".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 {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)
Run Code Online (Sandbox Code Playgroud)

注意事项

  • 稍微改变了子进程的生成方式:所有子进程都是首先创建的然后才启动

  • 添加了来自每个进程的一些打印调用,以跟踪它们在StdOut中的活动- 还添加了一些time.sleep调用(1 秒),以避免产生过多的输出

  • 最重要的是- 主进程不再永远运行。在某些时候,它会优雅地退出(3个周期后 - 由于计数器变量),并且当我之前提到的行为开始时,
    这也可以通过拦截TERM信号(以及其他可以由Kill命令)然后执行清理 - 这样子进程在杀死主进程时也会被杀死 - 但这更复杂

  • 我稍微简化了一下,只生成了 2 个孩子

  • 移动了包含在条件中的函数(对于结构)中的所有内容,因此如果导入模块,if __name__ == "__main__":则不会生成进程

  • 为每个子进程赋予不同的值proc.daemon,然后监视输出和ps -ef | grep "code00.py"输出

  • 子函数添加了一个参数(名称) ,但这仅用于显示目的

输出

[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] 064bit 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.
Run Code Online (Sandbox Code Playgroud)