【问题标题】:Subprocess dies when thread dies线程死亡时子进程死亡
【发布时间】:2018-02-22 01:36:41
【问题描述】:

我有一个程序可以触发 Python 计时器以生成子进程。一旦我的程序被终止或杀死,这些子进程应该被终止。为了做到这一点,我使用了“prctl hack”,它设置了孩子在父母去世后应该收到的信号。我得到的不良行为是:即使我的主进程正在运行,孩子们也会被杀死。下面的代码重现了这个问题:

from threading import Timer
import time
import os
import subprocess
import ctypes
import signal

def set_pdeathsig():
        print("child PID: %d" % os.getpid())
        print("child's parent PID: %d" % os.getppid())
        prctl = ctypes.CDLL("libc.so.6").prctl
        PR_SET_PDEATHSIG = 1
        prctl(PR_SET_PDEATHSIG, signal.SIGTERM)

def thread1():
        subprocess.Popen(['sleep', 'infinity'], preexec_fn=set_pdeathsig)
        time.sleep(10)
        print("thread 1 finished")

def thread2():
        subprocess.Popen(['sleep', 'infinity'], preexec_fn=set_pdeathsig)
        time.sleep(10)
        print("thread 2 finished")

print("main thread PID: %d" % os.getpid())

t1 = Timer(1, thread1)
t2 = Timer(1, thread2)

t1.start()
t2.start()

time.sleep(100)

您可以注意到,在线程终止之前,sleep 进程仍在运行。定时器线程死亡后,其各自的子进程也会死亡,即使主线程还活着。

【问题讨论】:

  • 显然你没有调用函数os.setpgid
  • 感谢@TheophileDano,这只是之前测试的代码。那不应该在那里。如果我删除它,问题仍然存在。

标签: python python-3.x subprocess python-multithreading


【解决方案1】:

这是预期的甚至记录在案的行为。来自 prctl(2) 的手册页:

      Warning: the "parent" in this case is considered to be the
      thread that created this process.  In other words, the signal
      will be sent when that thread terminates (via, for example,
      pthread_exit(3)), rather than after all of the threads in the
      parent process terminate.

这意味着您需要在其他地方生成子流程。如果您在退出的线程中执行此操作,那么您的子进程会按预期死亡,并且无法解决。

我会添加另一个线程并从那里启动进程。会不会有这样的工作:

from threading import Timer
from threading import Thread
import queue
import time
import os
import subprocess
import ctypes
import signal

def set_pdeathsig():
    print("child PID: %d" % os.getpid())
    print("child's parent PID: %d" % os.getppid())
    prctl = ctypes.CDLL("libc.so.6").prctl
    PR_SET_PDEATHSIG = 1
    prctl(PR_SET_PDEATHSIG, signal.SIGTERM)

def thread1(q):
    q.put(["sleep", "infinity"])
    time.sleep(5)
    print("thread 1 finished")

def thread2(q):
    q.put(["sleep", "infinity"])
    time.sleep(5)
    print("thread 2 finished")

def process_manager(q):
    while True:
        foo = q.get()
        subprocess.Popen(foo, preexec_fn=set_pdeathsig)

print("main thread PID: %d" % os.getpid())

qu = queue.Queue()
pm_thread = Thread(group=None, target=process_manager, args=(qu,))
pm_thread.daemon = True
pm_thread.start()


t1 = Timer(1, thread1, args=(qu,))
t2 = Timer(1, thread2, args=(qu,))

t1.start()
t2.start()

time.sleep(15)

这就是你想让它做的事情(Python3.5 用来测试)。当然,编排线程不适合可能是有原因的,但无论如何我还是将它作为解决方案候选者提供。现在您的子进程在 Timer 线程的死亡中幸存下来,但在您的主线程退出时仍将终止。

【讨论】: