子进程在线程死亡时死亡

Subprocess dies when thread dies

我有一个程序可以触发 Python 计时器来生成子进程。一旦我的程序终止或终止,这些子进程就应该终止。为了做到这一点,我正在使用 "prctl hack",它设置 child 在其 parent 死亡后应该接收哪个信号。我得到的不良行为是:即使我的主要进程是 运行,children 也会被杀死。以下代码重现了该问题:

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 个进程仍然是 运行。计时器线程死后,其各自的子进程也死了,即使主线程还活着。

这是预期的甚至记录在案的行为。来自 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 线程死亡后仍然存在,但当您的主线程退出时仍将终止。