Проблема с выходом из демонизированного процесса - PullRequest
4 голосов
/ 20 июня 2009

Я пишу программу-демон, которая порождает несколько других дочерних процессов. После того, как я запустил сценарий stop, основной процесс продолжает работать, когда он намеревается выйти, это действительно смутило меня.

import daemon, signal
from multiprocessing import Process, cpu_count, JoinableQueue
from http import httpserv
from worker import work

class Manager:
    """
    This manager starts the http server processes and worker
    processes, creates the input/output queues that keep the processes
    work together nicely.
    """
    def __init__(self):
        self.NUMBER_OF_PROCESSES = cpu_count()

    def start(self):
        self.i_queue = JoinableQueue()
        self.o_queue = JoinableQueue()

        # Create worker processes
        self.workers = [Process(target=work,
                                args=(self.i_queue, self.o_queue))
                        for i in range(self.NUMBER_OF_PROCESSES)]
        for w in self.workers:
            w.daemon = True
            w.start()

        # Create the http server process
        self.http = Process(target=httpserv, args=(self.i_queue, self.o_queue))
        self.http.daemon = True
        self.http.start()

        # Keep the current process from returning
        self.running = True
        while self.running:
            time.sleep(1)

    def stop(self):
        print "quiting ..."

        # Stop accepting new requests from users
        os.kill(self.http.pid, signal.SIGINT)

        # Waiting for all requests in output queue to be delivered
        self.o_queue.join()

        # Put sentinel None to input queue to signal worker processes
        # to terminate
        self.i_queue.put(None)
        for w in self.workers:
            w.join()
        self.i_queue.join()

        # Let main process return
        self.running = False


import daemon

manager = Manager()
context = daemon.DaemonContext()
context.signal_map = {
        signal.SIGHUP: lambda signum, frame: manager.stop(),
        }

context.open()
manager.start()

Сценарий stop представляет собой однострочную строку os.kill(pid, signal.SIGHUP), но после этого дочерние процессы (рабочие процессы и процессы http-сервера) красиво завершаются, но основной процесс просто остается там, я не знаю, что хранит это от возвращения.

Ответы [ 2 ]

1 голос
/ 24 июня 2009

Вы создаете процесс http-сервера, но не join(). Что произойдет, если вместо выполнения os.kill() для остановки процесса http-сервера вы отправите ему дозорный сигнал остановки обработки (None, как вы отправляете рабочим), а затем выполните self.http.join()?

Обновление : Вам также необходимо отправить None стражу во входную очередь один раз для каждого рабочего . Вы можете попробовать:

    for w in self.workers:
        self.i_queue.put(None)
    for w in self.workers:
        w.join()

N.B. Причина, по которой вам нужны два цикла, состоит в том, что если вы поместите None в очередь в том же цикле, который выполняет join(), то None может быть получен работником, отличным от w, поэтому присоединение к w вызовет блокировку вызывающего абонента.

Вы не показываете код для рабочих или http-сервера, поэтому я предполагаю, что они хорошо себя ведут с точки зрения вызова task_done и т. Д., И что каждый рабочий завершит работу, как только увидит None, без get() -ing больше вещей из очереди ввода.

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

1 голос
/ 23 июня 2009

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

import signal

class Manager:
    """
    This manager starts the http server processes and worker
    processes, creates the input/output queues that keep the processes
    work together nicely.
    """
    def __init__(self):
        self.NUMBER_OF_PROCESSES = cpu_count()

    def start(self):

       # all your code minus the loop

       print "waiting to die"

       signal.pause()

    def stop(self):
        print "quitting ..."

        # all your code minus self.running


manager = Manager()

signal.signal(signal.SIGHUP, lambda signum, frame: manager.stop())

manager.start()

Одним предупреждением является то, что signal.pause () будет делать паузу для любого сигнала, поэтому вы можете изменить свой код соответствующим образом.

EDIT:

У меня прекрасно работает следующее:

import daemon
import signal
import time

class Manager:
    """
    This manager starts the http server processes and worker
    processes, creates the input/output queues that keep the processes
    work together nicely.
    """
    def __init__(self):
        self.NUMBER_OF_PROCESSES = 5

    def start(self):

       # all your code minus the loop

       print "waiting to die"
       self.running = 1
       while self.running:
           time.sleep(1)

       print "quit"



    def stop(self):
        print "quitting ..."

        # all your code minus self.running

        self.running = 0


manager = Manager()

context = daemon.DaemonContext()
context.signal_map = {signal.SIGHUP : lambda signum, frame: manager.stop()}

context.open()
manager.start()

Какую версию python вы используете?

...