2010-03-30 21 views
20

Quando si utilizza python-daemon, sto creando sottoprocessi likeso:Python-daemon non uccide i suoi figli

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

Quando ho uccidere il processo demoniaca genitore (cioè non un operaio), con un Ctrl-C o SIGTERM , ecc., i bambini non muoiono. Come si uccidono i bambini?

Il mio primo pensiero è quello di utilizzare atexit uccidere tutti i lavoratori, 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()) 

Tuttavia, i figli di demoni sono cose difficili da gestire, e mi piacerebbe essere obbligato per i pensieri e input su come questo dovrebbe essere fatto.

Grazie.

+11

Uccidere i tuoi figli sembra una cosa da "demoniaca" da fare ... – ewall

+0

Definitivamente. Questo daemon è * non * fino alle specifiche. –

+14

Non è questo Python?Non puoi semplicemente fare "dal male import infanticide" o qualcosa del genere? – Syntactic

risposta

31

Le opzioni disponibili sono un po 'limitate. Se il self.daemon = True nel costruttore per la classe Worker non risolve il problema e il tentativo di rilevare i segnali nel Parent (ovvero, SIGTERM, SIGINT) non funziona, potrebbe essere necessario provare la soluzione opposta - invece di fare in modo che il genitore uccida i figli , puoi far suicidare i bambini quando muore il genitore.

Il primo passaggio consiste nel dare il costruttore a Worker il PID del processo padre (è possibile farlo con os.getpid()). Poi, invece di solo facendo self.queue.get() nel ciclo dei lavoratori, fare qualcosa di simile:

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 

La soluzione di cui sopra controlla se il genitore PID è diverso da quello che era in origine (vale a dire, se il processo figlio era adottato da init o lauchd in seguito alla morte del genitore) - vedere reference. Tuttavia, se questo non funziona per qualche motivo lo si può sostituire con la seguente funzione (adattato da 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 

Ora, quando il padre muore (per qualsiasi motivo), i bambini lavoratori spontaneamente cadere come mosche - proprio come volevi, demone! :-D

+1

Soluzione intelligente e ottima spiegazione! –

+2

Mi piacerebbe davvero darti più di 1 punto per questa risposta: D – edomaur

+0

non è questo occupato-in attesa dato che queue.get non sta bloccando più? IMO sarebbe meglio inserire un oggetto speciale (come None) nella coda con atexit nel thread principale e lasciare che i figli sys.exit() ottengano l'oggetto None. – Cedric

2

Atexit non farà il trucco: viene eseguito solo dopo aver terminato correttamente la mancata ricezione del segnale. Vedere la nota nella parte superiore dello docs. È necessario impostare la gestione del segnale tramite uno dei due mezzi.

L'opzione più semplice suono: impostare il flag daemon sui vostri processi di lavoro, per http://docs.python.org/library/multiprocessing.html#process-and-exceptions

Un po 'più difficile dal suono opzione: PEP-3143 sembra implicare che c'è un modo integrato per collegare le esigenze programma di pulizia in Pitone demone.

+0

Grazie Arthur. Per inciso, una domanda correlata che ho postato è http://stackoverflow.com/questions/2546276/python-process-wont-call-atexit - tratta alcuni di questi argomenti. Mi piacerebbe conoscere lo schema per garantire che i bambini vengano uccisi (o che non diventino zombi) - Immagino che il flag del daemon multiprocessing faccia la differenza - copre tutti i casi? Quali casi? –

3

non puoi semplicemente memorizzare il PID genitore quando il bambino viene creato (diciamo in self.myppid) e quando self.myppid è spettacolo diverso da getppid() significa che il genitore è morto.

potresti anche utilizzare i segnali per evitare la necessità di continuare a controllare se il genitore è cambiato. Non conosco le specifiche del pitone ma qualcosa di simile a quanto descritto here (at the bottom of the page) potrebbe funzionare.

+1

Hmm, che in realtà * potrebbe * funzionare - 'os.getppid()' dovrebbe restituire 1 se il genitore muore (cioè, 'init' (per Linux) o' launchd' (per Mac OS X) adotta i bambini). –