2010-06-23 4 views
14

Ich habe einen Threaded Python-Daemon. Wie jeder gute Daemon möchte er alle seine Worker-Threads starten und dann warten, bis es beendet wird. Das normale Signal für die Beendigung ist SIGTERM, und in den meisten Sprachen würde ich warten, um auf ein Ereignis oder einen Mutex zu warten, so dass threading.Event Sinn für mich machte. Das Problem ist, dass Pythons Event Objekt und Unix-Signale nicht gut zusammen zu spielen scheinen.Warum führt die Verwendung von threading.Event dazu, dass SIGTERM nicht abgefangen wird?

Dies funktioniert wie erwartet, endet am SIGTERM:

import signal 
import time 

RUN = True 

def handle(a, b): 
    global RUN 
    print "handled" 
    RUN = False 

signal.signal(signal.SIGTERM, handle) 
while RUN: 
    time.sleep(0.250) 
print "Stopping" 

aber dies führt zu keiner SIGTERM geliefert wird (dh ganz abgesehen von Aufhören, "behandelt" wird nie gedruckt):

import signal 
import threading 

RUN_EVENT = threading.Event() 

def handle(a, b): 
    print "handled" 
    RUN_EVENT.set() 

signal.signal(signal.SIGTERM, handle) 
RUN_EVENT.wait() 
print "Stopping" 

Also meine Frage ist:

  1. Bin ich missbrauchen threading.Event in irgendeiner Weise?
  2. Wenn nicht, gibt es eine andere Alternative als den Poll-and-Sleep-Mechanismus aus dem ersten Beispiel?
  3. Auch wenn ich nicht bin, warum benutzt threading.Event den Signalhandler?

Antwort

13

Von Python documentation on signals:

Obwohl Python Signal-Handler asynchron bis genannt werden als der Python Benutzer betreffen, so können sie nur zwischen den „atomaren“ Anweisungen der Python-Interpreter auftreten. Dies bedeutet, dass Signale, die während langer Berechnungen ankommen, die rein in C implementiert sind (z. B. Übereinstimmungen mit regulären Ausdrücken auf großen Textkörpern), um eine willkürliche Zeitspanne verzögert werden können.

Ich testete verschiedene threading und thread Klassen und keiner von ihnen arbeiten, wie Sie es wollen - dies wahrscheinlich wegen ist wie Python verarbeitet Signale.

In signal gibt es jedoch eine pause() Funktion, die schläft, bis ein Signal vom Prozess empfangen wird. Ihr modifiziertes Beispiel würde wie folgt aussehen:

import signal 

RUN = True 

def handle(a, b): 
    global RUN 
    print "handled" 
    RUN = False 

signal.signal(signal.SIGTERM, handle) 
while RUN: 
    signal.pause() 
print "Stopping" 

Ich überprüfte es auf Linux, es funktioniert. Ich denke nicht, dass es als Poll-and-Sleep klassifiziert wird, wenn Ihre Anwendung nicht viele andere Signale verwendet.

+0

Lästiges Feature von Python, aber perfekte Lösung. Vielen Dank. Ich gestehe, dass es mir nicht in den Sinn gekommen ist, nach zusätzlichen Beschränkungen für den Signalgebrauch zu suchen, da ich wusste, dass das äquivalente C gut funktionieren würde. –

+3

Bedeutet dies, dass die einzige Möglichkeit, Signale zu verarbeiten, darin besteht, den Hauptthread (Eltern) den Signalen zuzuordnen (durch Blockieren von 'signal.pause()')? Was das bedeutet, ist, dass der Hauptthread nichts mehr tun kann. Mit anderen Worten, Sie können kein Master-/Worker-Modell haben (wo die 2 Threads miteinander kommunizieren), aber Sie benötigen ein Master/Worker + Worker-Modell (wo die 2 Arbeiter miteinander reden und der Master nichts tut). –