Python-демон не убивает своих детей - PullRequest
20 голосов
/ 30 марта 2010

При использовании python-daemon я создаю подпроцессы likeso:

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 , чтобы убить всех рабочих, likeo:

 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())

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

Спасибо.

Ответы [ 3 ]

31 голосов
/ 10 апреля 2010

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

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

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

3 голосов
/ 10 апреля 2010

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

Вы также можете использовать сигналы, чтобы избежать необходимости проверять, изменился ли родитель. Я не знаю специфики Python, но что-то вроде того, что описано здесь (внизу страницы) , может работать.

2 голосов
/ 07 апреля 2010

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

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

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

...