Подтвердить что ты не робот

Неблокирующий subprocess.call

Я пытаюсь сделать неблокирующий вызов подпроцесса для запуска slave.py script из моей программы main.py. Мне нужно передать args с main.py на slave.py один раз, когда он (slave.py) сначала запускается через subprocess.call после того, как этот slave.py запускается некоторое время, а затем завершает работу.

main.py
for insert, (list) in enumerate(list, start =1):

    sys.args = [list]
    subprocess.call(["python", "slave.py", sys.args], shell = True)


{loop through program and do more stuff..}

И мой подчиненный script

slave.py
print sys.args
while True:
    {do stuff with args in loop till finished}
    time.sleep(30)

В настоящее время slave.py блокирует main.py от выполнения остальных задач, я просто хочу, чтобы slave.py не зависел от main.py, как только я передал ей аргументы. Эти два сценария больше не нуждаются в общении.

Я нашел несколько сообщений в сети о неблокирующем subprocess.call, но большинство из них сосредоточено на необходимости общения с slave.py в какой-то момент, который мне сейчас не нужен. Кто-нибудь знает, как реализовать это простым способом...?

4b9b3361

Ответ 1

Вы должны использовать subprocess.Popen вместо subprocess.call.

Что-то вроде:

subprocess.Popen(["python", "slave.py"] + sys.argv[1:])

Из docs на subprocess.call:

Запустите команду, описанную args. Дождитесь завершения команды, затем верните атрибут returncode.

(Также не используйте список для передачи аргументов, если вы собираетесь использовать shell = True).


Здесь показан пример MCVE 1 который демонстрирует неблокирующий вызов суперпроцесса:

import subprocess
import time

p = subprocess.Popen(['sleep', '5'])

while p.poll() is None:
    print('Still sleeping')
    time.sleep(1)

print('Not sleeping any longer.  Exited with returncode %d' % p.returncode)

Альтернативный подход, основанный на более поздних изменениях языка python для обеспечения совместной подпрограммы parallelism:

# python3.5 required but could be modified to work with python3.4.
import asyncio

async def do_subprocess():
    print('Subprocess sleeping')
    proc = await asyncio.create_subprocess_exec('sleep', '5')
    returncode = await proc.wait()
    print('Subprocess done sleeping.  Return code = %d' % returncode)

async def sleep_report(number):
    for i in range(number + 1):
        print('Slept for %d seconds' % i)
        await asyncio.sleep(1)

loop = asyncio.get_event_loop()

tasks = [
    asyncio.ensure_future(do_subprocess()),
    asyncio.ensure_future(sleep_report(5)),
]

loop.run_until_complete(asyncio.gather(*tasks))
loop.close()

1 Протестировано на OS-X, используя python2.7 и python3.6

Ответ 2

Здесь три уровня тщательности.

Как сообщает mgilson, если вы просто замените subprocess.call на subprocess.Popen, оставив все остальное одинаковым, то main.py не будет ждать завершения slave.py до его продолжения. Этого может быть достаточно. Если вы заботитесь о процессах зомби, вы должны сохранить объект, возвращенный из subprocess.Popen, и в какой-то более поздний момент вызвать его метод wait. (Зомби автоматически уйдут, когда main.py выйдет, поэтому это серьезная проблема, если main.py работает очень долго и/или может создавать много подпроцессов.) И, наконец, если вы не хотите зомби но вы также не хотите решать, где выполнить ожидание (это может быть целесообразно, если оба процесса работают в течение длительного и непредсказуемого времени после этого), используйте python-daemon, чтобы подчиненный отключил себя от мастера - в этом случае вы можете продолжить использование subprocess.call в главном.