2010-06-23 12 views
14

J'ai un démon Python threadé. Comme tout bon démon, il veut lancer tous ses threads de travail, puis attendre jusqu'à ce qu'il soit dit de se terminer. Le signal normal pour la terminaison est SIGTERM, et dans la plupart des langues je tiens à terminer en attendant un événement ou mutex, donc en utilisant threading.Event fait sens pour moi. Le problème est que l'objet Event de Python et les signaux Unix ne semblent pas bien jouer ensemble.Pourquoi l'utilisation de threading.Event entraîne-t-elle l'absence de capture de SIGTERM?

Cela fonctionne comme prévu, se terminant le 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" 

mais les résultats ne SIGTERM être livré (c.-à-tout à fait en dehors de cesser de fumer, "manipulé" n'est imprimé):

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" 

Donc ma question est:

  1. Ai-je usé threading.Event en quelque sorte?
  2. Si je ne le suis pas, y a-t-il une alternative autre que le mécanisme d'interrogation et de sommeil du premier exemple?
  3. De même si je ne le suis pas, pourquoi l'utilisation de threading.Event tue-t-elle le gestionnaire de signal?

Répondre

13

De Python documentation on signals:

Bien que les gestionnaires de signaux Python sont appelés de manière asynchrone pour autant que l'utilisateur Python est concerné, ils ne peuvent se produire entre les instructions « atomiques » de l'interpréteur Python. Cela signifie que les signaux arrivant pendant de longs calculs implémentés purement en C (tels que les correspondances d'expressions régulières sur de grands corps de texte) peuvent être retardés pendant une durée arbitraire.

J'ai testé différentes classes threading et thread et aucun d'entre eux de travailler comme vous le voulez - c'est probablement en raison de la façon dont les signaux Python gère. En signal, il existe cependant une fonction pause() qui dort jusqu'à la réception d'un signal par le processus. Votre exemple modifié ressemblerait à ceci:

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" 

Je l'ai vérifié sur Linux, ça marche. Je ne pense pas qu'il classifie comme interroger et dormir plus si votre application n'utilise pas beaucoup d'autres signaux.

+0

Caractéristique gênante de Python, mais solution parfaite. Je vous remercie. Je dois avouer qu'il ne m'est pas venu à l'esprit de chercher des restrictions supplémentaires sur l'utilisation du «signal», puisque je savais que l'équivalent C fonctionnerait très bien. –

+3

Cela signifie-t-il que la seule façon de gérer les signaux est de dédier le thread principal (parent) aux signaux de capture (en bloquant 'signal.pause()')? Ce que cela implique est que le fil principal ne peut plus rien faire d'utile. En d'autres termes, vous ne pouvez pas avoir de modèle maître/ouvrier (où les deux threads se parlent) mais vous avez besoin d'un modèle maître/ouvrier + ouvrier (où les deux ouvriers se parlent et le maître ne fait rien. –