2010-03-30 2 views
20

При использовании python-daemon, я создаю подпроцессы likeso:Python-демон не убивает своих детей

import multiprocessing 

class Worker(multiprocessing.Process): 
    def __init__(self, queue): 
     self.queue = queue # we wait for things from this in Worker.run() 

    ... 

q = multiprocessing.Queue() 

with daemon.DaemonContext(): 
    for i in xrange(3): 
     Worker(q) 

    while True: # let the Workers do their thing 
     q.put(_something_we_wait_for()) 

Когда я убить демонических процесс родительского (т.е. не работник) с помощью Ctrl-C или SIGTERM и т. д., дети не умирают. Как убить детей?

Моя первая мысль использовать atexit убить всех рабочих, likeso:

with daemon.DaemonContext(): 
    workers = list() 
    for i in xrange(3): 
     workers.append(Worker(q)) 

    @atexit.register 
    def kill_the_children(): 
     for w in workers: 
      w.terminate() 

    while True: # let the Workers do their thing 
     q.put(_something_we_wait_for()) 

Однако дети демонами хитрые вещи, чтобы справиться, и я обязан за мысли и о том, как вход это должно быть сделано.

спасибо.

+11

Убийство ваших детей действительно похоже на «демоническую» вещь ... – ewall

+0

Определенно. Этот демон * не * до спецификации. –

+14

Разве это не Python?Вы не можете просто «избавиться от инфантицида» или что-то еще? – Syntactic

ответ

31

Ваши варианты немного ограничены. Если выполнение self.daemon = True в конструкторе класса Worker не решит вашу проблему, и попытка поймать сигналы в родительском (т. Е. SIGTERM, SIGINT) не работает, возможно, вам придется попробовать противоположное решение - вместо того, чтобы родитель убил детей , вы можете заставить детей совершить самоубийство, когда родитель умирает.

Первый шаг - дать конструктору WorkerPID родительского процесса (вы можете сделать это с помощью os.getpid()). Затем, вместо того, чтобы просто делать self.queue.get() в цикле рабочего, сделать что-то вроде этого:

waiting = True 
while waiting: 
    # see if Parent is at home 
    if os.getppid() != self.parentPID: 
     # woe is me! My Parent has died! 
     sys.exit() # or whatever you want to do to quit the Worker process 
    try: 
     # I picked the timeout randomly; use what works 
     data = self.queue.get(block=False, timeout=0.1) 
     waiting = False 
    except queue.Queue.Empty: 
     continue # try again 
# now do stuff with data 

Решение выше проверки, чтобы увидеть, если родительский PID отличается от того, что он изначально был (то есть, если дочерний процесс был принятой init или lauchd, поскольку родитель умер) - см. reference. Однако, если это не работает по какой-то причине, вы можете заменить его на следующей функции (адаптировано из here):

def parentIsAlive(self): 
    try: 
     # try to call Parent 
     os.kill(self.parentPID, 0) 
    except OSError: 
     # *beeep* oh no! The phone's disconnected! 
     return False 
    else: 
     # *ring* Hi mom! 
     return True 

Теперь, когда родитель умирает (по любой причине), ребенок Рабочие самопроизвольно падать как мухи - так, как вы хотели, вы демон! :-D

+1

Умный раствор и отличное объяснение! –

+2

Я действительно хотел бы дать вам более 1 балла за этот ответ: D – edomaur

+0

не так занят, так как queue.get больше не блокируется? ИМО было бы лучше вставить в очередь специальный объект (например, None) в atexit в основном потоке и разрешить детям sys.exit(), если они получат объект None. – Cedric

2

Atexit не будет делать трюк - он запускается только при успешном завершении без сигнала - см. Примечание в верхней части docs. Вам необходимо настроить обработку сигналов одним из двух способов.

Чем проще звучащий вариант: установить флаг демона на ваших рабочих процессов, в http://docs.python.org/library/multiprocessing.html#process-and-exceptions

несколько сложнее звучащий вариант: PEP-3143 кажется, подразумевает, есть встроенный способ зацепить потребности программы очистки в python- демон.

+0

Спасибо Артур. Кстати, связанный с этим вопрос, который я опубликовал, - http://stackoverflow.com/questions/2546276/python-process-wont-call-atexit - он охватывает некоторые из этих тем. Я хотел бы узнать шаблон, обеспечивающий, чтобы дети были убиты (или не стали зомби) - я думаю, что флаг мультипроцессорного демона будет иметь значение - он охватывает все случаи? Какие случаи? –

3

вы не можете просто сохранить родительский идентификатор процесса, когда ребенок сначала создается (скажем, в self.myppid), и когда self.myppid является Diferent от getppid() означает, что родитель умер.

Вы также можете использовать сигналы, чтобы избежать необходимости продолжать проверку, изменился ли родитель. Я не знаю особенностей python, но что-то вроде того, что описано here (at the bottom of the page), может работать.

+1

Хм, это на самом деле * может * работать - 'os.getppid()' должен возвращать 1, если родительский умирает (т.е. 'init' (для Linux) или' launchd' (для Mac OS X) принимает детей). –