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

时间:2018-04-02 03:39:43

标签: python process daemon

以下是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个子进程和一个主进程。 我杀死了主要进程,但是4个孩子没有退出。

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

2 个答案:

答案 0 :(得分:2)

备注

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

事情并非如此。文档([Python 2]: daemon)应该更具体。

事情是多处理注册清理功能以在退出时杀死所有 deamonic 子项。这是通过[Python 2]: atexit - Exit handlers

完成的
  

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

您无法处理 TERM 信号(默认情况下由 kill 命令发送),因此主进程不会调用清理功能(离开它的孩子们跑步。)

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

code.py

#!/usr/bin/env python2

import multiprocessing
import os
import time


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


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


if __name__ == "__main__":
    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.format("Main", os.getpid(), os.getppid()))
        time.sleep(1)
        counter += 1

备注

  • 更改了子进程的生成方式:所有这些进程都创建了1 st ,然后才开始
  • 从每个进程添加了一些打印调用,以跟踪他们在 stdout 中的活动 - 还添加了一些time.sleep次调用(1秒),以避免产生输出太多
  • 最重要的 - 主要流程不再永远运行。在某些时候它会优雅地退出(在3个周期之后 - 由于计数器变量),并且当我之前提到的行为开始时就会出现。
    这也是可能的通过拦截 TERM 信号(以及其他可以通过 kill 命令明确发送的信号)并执行清理 - 这样孩子们也会被杀死杀死主要过程 - 但这更复杂
  • 我简化了一些事情,只产生了2个孩子
  • if __name__ == "__main__":中附上所有内容,以便在导入模块
  • 时不会生成流程
  • 为每个孩子提供不同的值proc.daemon,然后监控输出和ps -ef | grep "code.py"输出
  • child func中添加了一个参数(名称),但仅用于显示目的

<强>输出

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049604997]> python code.py
Process Child1 daemon: True
Process Child2 daemon: True
Output from process Main - pid: 20593, ppid: 12794
Output from process Child2 - pid: 20595, ppid: 20593
Output from process Child1 - pid: 20594, ppid: 20593
Output from process Main - pid: 20593, ppid: 12794
Output from process Child2 - pid: 20595, ppid: 20593
Output from process Child1 - pid: 20594, ppid: 20593
Output from process Main - pid: 20593, ppid: 12794
Output from process Child2 - pid: 20595, ppid: 20593
Output from process Child1 - pid: 20594, ppid: 20593

答案 1 :(得分:0)

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

我刚刚添加了一些睡眠语句来调试输出(没有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