Сценарий python должен запускать несколько подпроцессов через fork (). Все эти дочерние процессы должны выполняться одновременно, а родительский процесс должен ждать их завершения. Было бы неплохо иметь возможность установить тайм-аут для «медленного» ребенка. Родительский процесс продолжает обработку остальной части скрипта после того, как все дочерние элементы собраны.
Как лучше всего это решить? Спасибо.






Вы смотрели модуль пипроцессинг?
Традиционный UNIX-y способ взаимодействия с подпроцессами - открыть каналы для их стандартного ввода / вывода и использовать системный вызов select() для мультиплексирования связи в родительском процессе (доступно в Python через ... модуль select). .
Если вам нужно убить медленно работающий дочерний процесс, вы можете просто сохранить его идентификатор процесса (возвращенный вызовом os.fork()), а затем использовать os.kill(), чтобы убить его, когда он больше не нужен. Конечно, было бы проще иметь возможность явно связываться с дочерним процессом и скажи это, чтобы отключиться.
Простой пример:
import os
chidren = []
for job in jobs:
child = os.fork()
if child:
children.append(child)
else:
pass # really should exec the job
for child in children:
os.waitpid(child, 0)
Задержать медлительного ребенка - немного больше; вы можете использовать wait вместо waitpid и отбирать возвращаемые значения из списка дочерних элементов вместо ожидания каждого из них по очереди (как здесь). Если вы настроили alarm с обработчиком SIGALRM, вы можете прекратить ожидание после указанной задержки. Это все стандартные файлы UNIX, а не специфичные для Python ...
@zuo Дочерний процесс должен exec (заменяя его дочерним процессом, который нужно запустить) или _exit.
Эфемиент: каждый дочерний элемент в вашем коде останется в цикле for после завершения своей работы. Он будет разветвляться снова и снова. Более того, дочерние элементы, которые запускаются, когда children [] не пусто, будут пытаться дождаться некоторых из своих братьев в конце цикла. В конце концов кто-нибудь разобьется. Это обходной путь:
import os, time
def doTheJob(job):
for i in xrange(10):
print job, i
time.sleep(0.01*ord(os.urandom(1)))
# random.random() would be the same for each process
jobs = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
imTheFather = True
children = []
for job in jobs:
child = os.fork()
if child:
children.append(child)
else:
imTheFather = False
doTheJob(job)
break
# in the meanwhile
# ps aux|grep python|grep -v grep|wc -l == 11 == 10 children + the father
if imTheFather:
for child in children:
os.waitpid(child, 0)
Как говорится в комментарии в моем исходном коде, необходимо заменить «pass» на «exec (job) or similar». Если это будет сделано, все будет работать должным образом. Кроме того, вам лучше использовать os.exit () вместо установки imTheFather.
@ephemient Но почему os._exit() лучше?
@AloisMahdal используется для выхода из разветвленного дочернего процесса docs.python.org/2/library/os.html#os._exit
Я делал это на Perl раз или два. Изучал python и хотел воспроизвести функцию. Планировщик для неизвестного количества разветвленных задач должен отслеживать запущенные задачи, завершенные задачи и коды возврата. Этот код включает код для обработчика SIGCHLD, родительской задачи и простой дочерней задачи.
#!/usr/bin/env python3
import signal, traceback
import os, subprocess
import time
#
# sigchild handler for reaping dead children
#
def handler(signum, frame):
#
# report stat of child tasks
print(children)
#
# use waitpid to collect the dead task pid and status
pid, stat = os.waitpid(-1, 0)
term=(pid,stat)
print('Reaped: pid=%d stat=%d\n' % term)
#
# add pid and return code to dead kids list for post processing
ripkids.append(term)
print(ripkids)
print('\n')
#
# update children to remove pid just reaped
index = children.index(pid)
children.pop(index)
print(children)
print('\n')
# Set the signal handler
signal.signal(signal.SIGCHLD, handler)
def child():
print('\nA new child ', os.getpid())
print('\n')
time.sleep(15)
os._exit(0)
def parent():
#
# lists for started and dead children
global children
children = []
global ripkids
ripkids = []
while True:
newpid = os.fork()
if newpid == 0:
child()
else:
pidx = (os.getpid(), newpid)
children = children+[newpid]
print("parent: %d, child: %d\n" % pidx)
print(children)
print('\n')
reply = input("q for quit / c for new fork")
if reply == 'c':
continue
else:
break
parent()
должен ли дочерний процесс завершиться после выполнения?