如何等待子进程的启动?

3

我执行一个进程(使用Popen),一段时间后,这个进程会执行一个子进程。
我的目标是等待子进程被生成,然后继续执行我的脚本。

我找到的唯一方法是使用psutil并轮询,直到该进程有子进程为止。

process = psutil.Process(subprocess.Popen(command).pid)
while 0 == len(process.children()):
    time.sleep(0.2)

有没有比轮询子元素更好的方法?也许等待某些事件?

我正在使用Windows 10。


第一个子进程在生成自己的子进程后,是否有选项在其标准输出或输出错误上写入一些内容?或者使用其他方式来发出操作信号? - Serge Ballesta
没有任何特殊事件,当一个进程创建另一个进程时(只有内核模式回调被调用)。 - RbMm
@SergeBallesta 在生成子进程之后,进程可以执行一些操作(例如将某些内容打印到标准输出)。 - cydan
1
@RbMm,但是有一个事件可以将进程添加到作业。子进程可以被挂起并添加到不允许逃脱的作业中,并与完成端口关联。然后,当子进程恢复并生成另一个进程时,作业会向完成端口发布“JOB_OBJECT_MSG_NEW_PROCESS”,其中包含孙子进程的PID。祖父进程通过“GetQueuedCompletionStatus”等待此消息。 - Eryk Sun
@eryksun - 是的,你说得对。在几乎所有情况下,这都是一个不错的解决方案。除非子进程试图从任务分离创建进程。 - RbMm
显示剩余2条评论
4个回答

1

根据您的评论,如果您可以让启动子进程的过程在启动时向STDOUT写入一些内容,那么您可以轻松使用Popen.check_output()从父进程中获取STDOUT字符串。例如,如果您的子进程(即command)向STDOUT写入Subprocess started\n,则您可以纯粹通过subprocess模块来实现:

import subprocess

response = subprocess.check_output(command)
if response.rstrip() == "Subprocess started":
    print("Woo! Our sub-subprocess was started...")

然而,如果你的命令返回多个输出,你可能需要筛选出你不感兴趣的那些。你可以通过观察子进程的STDOUT来实现这一点,例如观察上述的Subprocess started字符串:
import subprocess

proc = subprocess.Popen(command, stdout=subprocess.PIPE)
while True:  # a loop to read our subprocess STDOUT
    line = proc.stdout.readline().rstrip()  # read line-by-line
    if line == "Subprocess started":
        print("Woo! Our sub-subprocess was started...")
        break  # no need to read the STDOUT anymore

如果您预计从命令接收错误消息(例如,无法启动子进程或类似情况),您还可以捕获STDERR(或将其管道传输到STDOUT)。


问题在于,我仍然需要等待输出,因此我仍然需要使用time.sleep进行轮询。 - cydan
@cydan - 在你的子进程的标准输出行缓冲区被填满(至少一行)之前,你不需要做任何事情,所有这些都将等待它(proc.stdout.readline()是一个阻塞操作)。如果你的子进程只期望向其标准输出写入一次内容,那么subprocess.check_output()也是阻塞的。 - zwer
与其为此任务过载标准I/O,不如创建一个可继承的管道,并将句柄值作为命令行参数或环境变量传递。如果它也是一个Python进程,子进程可以通过msvcrt.open_osfhandle将句柄关联为文件描述符,之后可以使用常规的Python I/O函数。或者直接通过WinAPI WriteFile直接使用句柄。 - Eryk Sun

0

我认为这没有更好的解决方案。但是你可以优化进程调用:

process = psutil.Popen(command)

Popen类

这是一个更方便的标准库subprocess.Popen接口。它启动一个子进程,您可以像使用subprocess.Popen一样处理它,但它还提供了psutil.Process类的所有方法。[...]

此外,您可以使用自己编写的带有等待函数的事件。等待函数需要更多的CPU资源,但响应更快。请参见此处的示例。但这取决于您的Python版本。


0
如果第一个子进程可以生成自己的子进程并在此之后写入内容,那么主脚本可以等待第一次写入。
以下是一个工作示例(环境为Windows上的Python 2.7):
Python主脚本:
python = r"C:\Python27\python.exe"
script = r"...\foo.py"
deb = time.clock()
child = subprocess.Popen([ python, script], stdout = subprocess.PIPE)
cr = child.stdout.read()
fin = time.clock()
print fin-deb, cr

Python(简化版)的第一个子代:

time.sleep(2)  # add a delay
# the child is supposed to be spawned here
print "Done"
sys.stdout.flush()  # flushes stdout so that caller gets it immediately
time.sleep(8)       # just to make the child live a little longer

主脚本输出为:

2.12153148707 Done

证明主代码在第一次休眠后继续执行。

0
如果您可以控制子进程,您可以在父进程和子进程之间使用一些同步原语,例如使用套接字或文件。否则,在繁忙的循环中轮询children()是您唯一能做的事情,只需确保在一段时间后退出while循环,否则您的程序可能会无限期地挂起。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接