2009-06-20 6 views
4

J'écris un programme démon qui engendre plusieurs autres processus enfants. Après avoir exécuté le script stop, le processus principal continue de s'exécuter quand il est destiné à quitter, cela m'a vraiment perturbé.Problème avec la sortie d'un processus démonisé

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

Le script stop est juste un one-liner os.kill(pid, signal.SIGHUP), mais après que les processus enfants (processus de travail et processus de serveur http) fin bien, mais le processus principal reste juste là, je ne sais pas ce qui maintient à partir de revenir.

+0

Voyez-vous le "quitter ..." imprimé? – grieve

+1

J'ai essayé votre code tel quel, sans le module démon, et cela fonctionne pour moi. Pouvez-vous donner un lien vers votre version du module démon? Une recherche google révèle quelques choix. – grieve

+0

Désolé pour ma réponse tardive, pour le module démon j'utilise http://pypi.python.org/pypi/python-daemon/ – btw0

Répondre

1

J'ai essayé une approche différente, et cela semble fonctionner (notez que j'ai enlevé les portions de démon du code car je n'avais pas ce module installé).

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

Un avertissement, est que signal.pause() sera réactivez pour tout signal, vous voudrez peut-être changer votre code en conséquence.

EDIT:

Les oeuvres suivantes très bien pour moi:

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

Quelle version de python utilisez-vous?

+0

Je ne fais pas moi-même la manipulation du signal manuellement, elle est traitée avec le module démon de http://pypi.python.org/pypi/python-daemon/ – btw0

1

Vous créez le processus du serveur http mais vous ne le définissez pas join(). Que se passe-t-il si, au lieu de faire un os.kill() pour arrêter le processus du serveur http, vous lui envoyez une sentinelle d'arrêt de traitement (None, comme vous l'envoyez aux travailleurs), puis faites un self.http.join()?

Mise à jour: Vous devez également envoyer la None sentinelle à la file d'attente d'entrée une fois pour chaque travailleur. Vous pouvez essayer:

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

N.B. La raison pour laquelle vous avez besoin de deux boucles est que si vous placez le None dans la file d'attente dans la même boucle que le join(), ce None peut être ramassé par un travailleur autre que w, donc rejoindre w provoquera le blocage de l'appelant.

Vous n'affichez pas le code pour les serveurs de travail ou http, donc je suppose qu'ils sont bien comportés en termes d'appel à task_done etc. et que chaque travailleur quittera dès qu'il verra un None, sans get() -ing plus de choses de la file d'attente d'entrée.

Notez également qu'il existe au moins one open, hard-to-reproduce issue avec JoinableQueue.task_done(), ce qui peut vous mordre.

+0

La boucle while while dans mon code était en effet self.http.join(), mais un processus n'a pas quitté, alors je l'ai remplacé par une boucle while while, le processus ne s'arrêtant toujours pas :( – btw0

+0

Voir ma mise à jour Une version modifiée de votre script avec mes modifications se termine correctement, au moins dans mon environnement . –

Questions connexes