Python-daemon tötet seine Kinder nicht
-
23-09-2019 - |
Frage
Beim Benutzen Python-daemon, Ich erstelle Subprozesse 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())
Wenn ich den Elternprozess (dh kein Arbeiter) mit einem Strg-C oder Sigterm usw. töte, sterben die Kinder nicht. Wie tötet man die Kinder?
Mein erster Gedanke ist zu verwenden ATEXIT Um alle Arbeiter zu töten, 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())
Die Kinder von Daemons sind jedoch schwierige Dinge, und ich wäre für Gedanken und Eingaben, wie dies getan werden sollte, verpflichtet.
Vielen Dank.
Lösung
Ihre Optionen sind etwas begrenzt. Wenn self.daemon = True
im Konstruktor für die Worker
Die Klasse löst Ihr Problem nicht und versucht, Signale im Elternteil zu fangen (dh,,, SIGTERM, SIGINT
) funktioniert nicht, Sie müssen möglicherweise die entgegengesetzte Lösung ausprobieren. Anstatt die Eltern die Kinder zu töten, können Sie die Kinder bei sterben.
Der erste Schritt besteht darin, dem Konstruktor zu geben Worker
das PID
des übergeordneten Prozesss (Sie können dies mit tun os.getpid()
). Dann, anstatt nur zu tun self.queue.get()
Machen Sie in der Arbeiterschleife so etwas:
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
Die obige Lösung überprüft, ob sich die Eltern -PID von der ursprünglich unterscheidet (dh, wenn der Kinderprozess von übernommen wurde von init
oder lauchd
Weil der Elternteil gestorben ist) - siehe Hinweis. Wenn das jedoch aus irgendeinem Grund nicht funktioniert, können Sie es durch die folgende Funktion ersetzen (angepasst von hier):
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
Wenn der Elternteil nun stirbt (aus irgendeinem Grund), werden die Kinderarbeiter spontan wie Fliegen fallen - genau wie Sie wollten, Ihr Daemon! :-D
Andere Tipps
Kannst du nicht einfach die übergeordnete PID speichern, wenn das Kind zum ersten Mal erstellt wird (sagen wir in self.myppid
) und wann self.myppid
ist unterschiedlich von getppid()
bedeutet, dass der Elternteil gestorben ist.
Sie können auch Signale verwenden, um zu vermeiden, dass sich weiterhin geändert hat, ob sich der Elternteil geändert hat. Ich kenne die Python -Einzelheiten nicht, aber etwas entlang, wie es beschrieben wird Hier (unten auf der Seite) könnte funktionieren.
Atexit wird den Trick nicht ausführen-es wird nur nach einer erfolgreichen nicht signalen Beendigung ausgeführt-siehe die Notiz oben in der Spitze der Dokumente. Sie müssen die Signalhandhabung über eine von zwei Mitteln einrichten.
Die einfachere Option: Setzen Sie das Dämon-Flag auf Ihren Arbeitsprozessen per Figur http://docs.python.org/library/multiprocessing.html#process-and-eceptions
Etwas härter klingende Option: PEP-3143 Es scheint zu implizieren, dass es einen integrierten Weg gibt, um das Programm in Python-Daemon zu säubern.