当 parent 离开僵尸时,Python subprocess.communicate 挂起

标签 python multithreading popen python-multiprocessing python-multithreading

我正在尝试使用 Popen 创建子进程 A 以及使用 Popen.communicate 与之通信的线程。主进程将使用 Thread.join 等待指定超时的线程,并在超时到期后杀死 A,这也会导致线程死亡。

但是,当 A 本身产生更多子进程 BCD 时,这似乎不起作用> 与拒绝死亡的 A 具有不同的进程组。即使在 A 已死并标记为 defunct 之后,甚至在主进程使用 os.waitpid() 获取 A 以便它不再存在之后, 线程拒绝加入主线程。

只有在所有 child BCD 都被杀死后,Popen.communicate 才会结束返回。

这种行为实际上是模块所期望的吗?递归等待在某些情况下可能很有用,但作为 Popen.communicate 的默认行为肯定不合适。如果这是预期的行为,是否有任何方法可以覆盖它?

这是一个非常简单的例子:

from subprocess import PIPE, Popen
from threading import Thread
import os
import time
import signal

DEVNULL = open(os.devnull, 'w')

proc = Popen(["/bin/bash"], stdin=PIPE, stdout=PIPE,
             stderr=DEVNULL, start_new_session=True)


def thread_function():
    print("Entering thread")
    return proc.communicate(input=b"nohup sleep 100 &\nexit\n")


thread = Thread(target=thread_function)
thread.start()
time.sleep(1)
proc.kill()
while True:
    thread.join(timeout=5)
    if not thread.is_alive():
        break
    print("Thread still alive")

这是在 Linux 上。

最佳答案

我认为这来自于在 Linux 中编写 popen.communicate 方法的一种相当自然的方式。 Proc.communicate() 似乎读取 stdin 文件描述符,当进程结束时它将返回 EOF。然后等待获取进程的退出代码。

在您的示例中, sleep 进程从 bash 进程继承标准输入文件描述符。因此,当 bash 进程终止时, popen.communicate 不会在 stdin 管道上获得 EOF,因为 sleep 仍然打开它。解决此问题的最简单方法是将通信线路更改为:

return proc.communicate(input=b"nohup sleep 100 >/dev/null&\nexit\n")

这会导致您的线程在 bash 终止时立即结束...由于退出,而不是您的 proc.kill,在这种情况下。但是,如果您使用 exit 语句或 proc.kill 调用,则在 bash 结束后 sleep 仍在运行。如果你也想打发 sleep ,我会用

os.killpg(proc.pid,15)

而不是 proc.kill()。如果B、C和D改变组,则杀死B,C和D的更普遍的问题是更复杂的问题。

附加数据: 我找不到关于这种 proc.communicate 方法的任何官方文档,但我忘记了最明显的地方:-) 我是在 this answer 的帮助下找到的。 . docs for communicate说:

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate.

您卡在第 2 步:读取文件末尾,因为 sleep 使管道保持打开状态。

关于当 parent 离开僵尸时,Python subprocess.communicate 挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48743309/

相关文章:

python - Django channel VS Django 3.0/3.1?

notifyAll() 方法上的 java.lang.IllegalMonitorStateException

java - 访问匿名内部类中封闭范围的成员变量

Python子进程超时?

python - 在 Python 中执行 N*M 迭代的最快算法

python - 将基于 ctypes 的代码片段从 Linux 移植到 Windows

在 C 中使用 popen 捕获 tshark 标准输出

python - subprocess.Popen 不是线程安全的吗?

python - 通过列名称和观察结果比较数据框

c# - 异步等待 block 主 UI