Как дождаться начала дочернего процесса?

Я запускаю процесс (используя Popen), и через некоторое время этот процесс выполняет дочерний процесс.
Что я пытаюсь сделать, так это дождаться появления дочернего процесса, а затем продолжить выполнение моего сценария.

Единственный способ, который я нашел, - это использовать psutil и опрос, пока у процесса не появятся дети.

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

Есть ли лучший способ сделать это, чем опрос детей? Может, ждете какого-то события?

Я использую для этого Windows 10.


person cydan    schedule 26.07.2017    source источник
comment
Есть ли вариант, что первый дочерний элемент пишет что-то на свой стандартный вывод или выводит ошибку после порождения собственного дочернего элемента? Или использовать любой другой способ, чтобы сигнализировать об операции.?   -  person Serge Ballesta    schedule 26.07.2017
comment
никак иначе. никаких специальных событий, когда один процесс создает другой (вызывается только обратный вызов режима ядра)   -  person RbMm    schedule 26.07.2017
comment
@SergeBallesta Процесс может что-то сделать после создания дочернего процесса. (Например, распечатать что-то на стандартный вывод)   -  person cydan    schedule 26.07.2017
comment
@RbMm, но есть событие для добавления процесса в задание. Дочерний процесс можно создать приостановленным и добавить к заданию, которое не допускает отрыва и которое связано с портом завершения. Затем, когда дочерний процесс возобновляется и, в свою очередь, порождает другой процесс, задание отправит JOB_OBJECT_MSG_NEW_PROCESS в порт завершения, который будет содержать PID дочернего процесса. Прародительский процесс ожидает этого сообщения через GetQueuedCompletionStatus.   -  person Eryk Sun    schedule 26.07.2017
comment
@eryksun - я имею в виду в общем случае, если особый дизайн процесса для уведомления о создании дочернего элемента - другая ситуация   -  person RbMm    schedule 26.07.2017
comment
@RbMm, это не требует модификации ни дочернего, ни дочернего процесса. Все это обрабатывается объектом Windows Job.   -  person Eryk Sun    schedule 26.07.2017
comment
@eryksun - да, ты прав. почти во всех случаях это будет хорошим решением. если ребенок не пытается создать процесс, отсоединенный от задания   -  person RbMm    schedule 26.07.2017


Ответы (4)


Основываясь на вашем комментарии о том, что вы можете заставить процесс, который вы запускаете, записывать что-то в 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...")

Однако, если ваш command возвращает несколько выходных данных, вам, возможно, придется отсеять те, которые вас не интересуют. Вы можете сделать это, наблюдая за 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), если вы ожидаете получать сообщения об ошибках от вашего command (например, не можете запустить подпроцесс или что-то в этом роде).

person zwer    schedule 26.07.2017
comment
Проблема в том, что мне все еще нужно ждать вывода, поэтому мне все еще нужно опросить, используя time.sleep - person cydan; 26.07.2017
comment
@cydan - вам не нужно ничего делать, пока буфер строки STDOUT ваших подпроцессов не будет заполнен (по крайней мере, одна строка), все это будет ждать (proc.stdout.readline() - блокирующее действие). subprocess.check_output() также блокируется, если ожидается, что ваш подпроцесс запишет что-то в свой STDOUT только один раз. - person zwer; 26.07.2017
comment
Вместо того, чтобы перегружать стандартный ввод-вывод для этой задачи, вы можете создать наследуемый канал и передать значение дескриптора в качестве аргумента командной строки или переменной среды. Если это также процесс Python, дочерний процесс может связать дескриптор как файловый дескриптор через msvcrt.open_osfhandle, после чего его можно будет использовать с обычными функциями ввода-вывода Python. Или просто используйте дескриптор напрямую через WinAPI WriteFile. - person Eryk Sun; 26.07.2017

Я думаю, что нет лучшего решения для этого. Но вы можете оптимизировать вызов процесса:

process = psutil.Popen(command)

Открытый класс

Более удобный интерфейс для stdlib subprocess.Popen. Он запускает подпроцесс, и вы работаете с ним точно так же, как при использовании subprocess.Popen, но, кроме того, он также предоставляет все методы класса psutil.Process. [...]

Кроме того, вы можете написать собственное событие с функцией ожидания. Подождите больше процессора, но он более отзывчив. См., например, здесь. Но это зависит от вашей версии Python.

person M Dennis    schedule 26.07.2017

Если первый дочерний элемент может что-то написать после создания собственного дочернего элемента, основной сценарий может дождаться первой записи.

Вот рабочий пример (среда Python 2.7 в Windows):

Основной скрипт 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

доказывая, что основной сценарий продолжался после первого сна.

person Serge Ballesta    schedule 26.07.2017

Если у вас есть контроль над дочерним процессом, вы можете использовать некоторый примитив синхронизации между родителем и дочерним элементом, например, используя сокет или файл, в противном случае опрос children() в цикле занятости - единственное, что вы можете сделать, просто убедитесь, что вышли цикл через некоторое время, или ваша программа может зависнуть на неопределенный срок.

person Giampaolo Rodolà    schedule 27.07.2017