gpt4 book ai didi

python - 如何等待子进程?

转载 作者:IT王子 更新时间:2023-10-29 01:15:02 29 4
gpt4 key购买 nike

我有一个 Python 脚本可以启动这样的任务:

import os
os.system("./a.sh")
do_c()

但是 a.sh是一个启动其他程序的 bash 脚本。 bash 脚本本身似乎在所有启动的脚本都准备好之前就准备好了。

如何在 do_c() 之前等待所有脚本(子进程)准备就绪被处决?

澄清:当我写好时,我的意思是完成/退出。

例子

运行文件

该文件可以更改。但是不要依赖 sleep ,因为我不知道多久 a.pyb.py拿。
#!/usr/bin/env python

import os
from time import sleep

print("Started run.py")
os.system("./a.py")
print("a is ready.")
print("Now all messages should be there.")

sleep(30)

一个.py

这可能无法修改:
#!/usr/bin/env python

import subprocess
import sys

print(" Started a.py")
pid = subprocess.Popen([sys.executable, "b.py"])
print(" End of a.py")

b.py

这可能无法修改:
#!/usr/bin/env python

from time import sleep

print(" Started b.py")
sleep(10)
print(" Ended b.py")

期望输出

最后一条消息必须是 Now all messages should be there. .

电流输出
started run.py
Started a.py
End of a.py
a is ready.
Now all messages should be there.
Started b.py
Ended b.py

最佳答案

处理这种情况的常用方法不起作用。等待 a.py (这是 os.system 默认情况下)不起作用,因为 a.py在其子进程执行完毕之前退出。查找 b.py 的 PID很棘手,因为,一旦 a.py退出,b.py不能再以任何方式连接到它 - 即使是 b.py 的父 PID是 1,init过程。

然而,有可能利用继承的文件描述符作为 child 死亡的穷人信号。设置一个管道,其读端在run.py ,其写端由a.py继承和它所有的 child 。只有当最后一个 child 退出时,管道的写端才会关闭,并且出现 read()在管道的读取端将停止阻塞。

这是run.py的修改版实现这个想法,显示所需的输出:

#!/usr/bin/env python

import os
from time import sleep

print("Started run.py")

r, w = os.pipe()
pid = os.fork()
if pid == 0:
os.close(r)
os.execlp("./a.py", "./a.py")
os._exit(127) # unreached unless execlp fails
os.close(w)
os.waitpid(pid, 0) # wait for a.py to finish
print("a is ready.")

os.read(r, 1) # wait for all the children that inherited `w` to finish
os.close(r)
print("Now all messages should be there.")

说明:

管道是一种进程间通信设备,它允许父进程和子进程通过继承的文件描述符进行通信。通常创建一个管道,fork 一个进程,可能执行一个外部文件,并从管道的读端读取一些数据,相同的数据由另一个写入管道的写端。 (Shell 使用此机制实现管道,方法是更进一步,并使标准文件描述符(例如 stdin 和 stdout)指向管道的适当末端。)

在这种情况下,我们不关心与 child 交换实际数据,我们只想在他们退出时得到通知。为了实现这一点,我们利用了这样一个事实,即当一个进程死亡时,内核会关闭它的所有文件描述符。反过来,当 fork 进程继承文件描述符时,当描述符的所有副本都关闭时,文件描述符就被认为是关闭的。因此,我们设置了一个带有写端的管道,该管道将被 a.py 产生的所有进程继承。 .这些进程不需要知道关于这个文件描述符的任何事情,唯一重要的是当它们都死了时,管道的写端就会关闭。这将在管道的读取端通过 os.read() 指示不再阻塞并返回表示文件结束条件的 0 长度字符串。

该代码是该想法的简单实现:
  • os.pipe()之间的部分和第一个 printos.system() 的实现,不同之处在于它关闭了 child 管道的读取端。 (这是必要的——简单地调用 os.system() 将使读取端保持打开状态,这将阻止父级中的最终读取正常工作。)
  • os.fork()复制当前进程,唯一区分父进程和子进程的方法是 家长 你得到子PID(并且子得到0,因为它总是可以使用os.getpid()找出它的PID)。
  • if pid == 0:分支在子进程中运行,只有 execs ./a.py . “Exec”意味着它运行指定的可执行文件而不返回。 os._exit()只有在这种情况下才存在 execlp失败(在 Python 中可能是不必要的,因为 execlp 的失败会引发一个异常,该异常会退出程序,但仍然如此)。程序的其余部分在父级中运行。
  • 父进程关闭管道的写端(否则尝试从读端读取会死锁)。 os.waitpid(pid)是等待a.py通常由 os.system() 执行.在我们的例子中,没有必要拨打 waitpid ,但这样做是一个好主意,以防止僵尸留下。
  • os.read(r, 1)这就是魔法发生的地方:它尝试从管道的读取端读取最多 1 个字符。由于从来没有人向管道的写端写入数据,因此读取将阻塞,直到管道的写端关闭。由于a.py的 child 对继承的文件描述符一无所知,关闭它的唯一方法是内核在相应进程死亡后执行此操作。当所有继承的写结束描述符都关闭时,os.read()返回一个零长度的字符串,我们忽略它并继续执行。
  • 最后,我们关闭管道的写端,从而释放共享资源。
  • 关于python - 如何等待子进程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22514121/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com