parallel - python multiprocessing windows
Python-daemon no mata a sus niƱos (3)
Cuando uso python-daemon , estoy creando subprocesos como:
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())
Cuando mato el proceso demoníaco de los padres (es decir, no un trabajador) con un Ctrl-C o SIGTERM, etc., los niños no mueren. ¿Cómo se mata a los niños?
Mi primer pensamiento es usar atexit para matar a todos los trabajadores, como:
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())
Sin embargo, los hijos de los demonios son difíciles de manejar, y me gustaría que me diera cuenta de cómo hacer esto.
Gracias.
Atexit no hará el truco, solo se ejecuta en una terminación exitosa sin señal, vea la nota cerca de la parte superior de los docs . Debe configurar el manejo de la señal a través de uno de dos medios.
La opción de sonido más fácil: establezca el indicador del demonio en sus procesos de trabajo, según http://docs.python.org/library/multiprocessing.html#process-and-exceptions
Opción un poco más dura: el PEP-3143 parece implicar que hay una forma integrada de enganchar las necesidades de limpieza del programa en el daemon de Python.
Sus opciones son un poco limitadas. Si hacer self.daemon = True
en el constructor para la clase Worker
no resuelve su problema y tratar de detectar señales en el Parent (es decir, SIGTERM, SIGINT
) no funciona, es posible que tenga que probar la solución opuesta, en lugar de Si los padres matan a los niños, puede hacer que los niños se suiciden cuando el padre muere.
El primer paso es darle al constructor de PID
el PID
del proceso principal (puede hacerlo con os.getpid()
). Luego, en lugar de simplemente hacer self.queue.get()
en el bucle de trabajo, haga algo como esto:
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 solución anterior verifica si el PID principal es diferente de lo que era originalmente (es decir, si el proceso secundario fue adoptado por init
o lauchd
porque el padre murió): consulte la reference . Sin embargo, si eso no funciona por alguna razón, puede reemplazarlo con la siguiente función (adaptada de 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
Ahora, cuando el Padre muere (por cualquier razón), los Trabajadores infantiles caerán espontáneamente como moscas, ¡tal como usted quería, demonio! :-D
no puede simplemente almacenar el pid padre cuando se crea el niño por primera vez (digamos en self.myppid
) y cuando self.myppid
es diferente de getppid()
significa que el padre murió.
También podría usar señales para evitar la necesidad de seguir verificando si el padre ha cambiado. No conozco los detalles específicos de python, pero algo como lo que se describe aquí (en la parte inferior de la página) podría funcionar.