Неблокирующий 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 в какой-то момент, который мне сейчас не нужен. Кто-нибудь знает, как реализовать это простым способом...?
Ответы
Ответ 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
в главном.