2009-05-04 7 views
10

Si j'ai un programme qui utilise threading et file d'attente, comment puis-je obtenir des exceptions pour arrêter l'exécution? Voici un exemple de programme, qui n'est pas possible d'arrêter avec ctrl-c (essentiellement extrait des docs python).Comment gérer les exceptions lors de l'utilisation du thread et de la file d'attente?

from threading import Thread 
from Queue import Queue 
from time import sleep 

def do_work(item): 
    sleep(0.5) 
    print "working" , item 

def worker(): 
     while True: 
      item = q.get() 
      do_work(item) 
      q.task_done() 

q = Queue() 

num_worker_threads = 10 

for i in range(num_worker_threads): 
    t = Thread(target=worker) 
    # t.setDaemon(True) 
    t.start() 

for item in range(1, 10000): 
    q.put(item) 

q.join()  # block until all tasks are done 

Répondre

6

La façon la plus simple est de commencer tous les threads de travail comme fils de démon, puis ont tout simplement votre boucle principale soit

while True: 
    sleep(1) 

Frapper les touches Ctrl + C lancera une exception dans votre thread principal, et tout des threads démon sortira à la sortie de l'interpréteur. Cela suppose que vous ne voulez pas effectuer de nettoyage dans tous ces threads avant qu'ils ne quittent.

Une façon plus complexe est d'avoir une approche globale stoppedEvent:

stopped = Event() 
def worker(): 
    while not stopped.is_set(): 
     try: 
      item = q.get_nowait() 
      do_work(item) 
     except Empty:  # import the Empty exception from the Queue module 
      stopped.wait(1) 

Ensuite, votre boucle principale peut régler le stopped événement à False quand il obtient un KeyboardInterrupt

try: 
    while not stopped.is_set(): 
     stopped.wait(1) 
except KeyboardInterrupt: 
    stopped.set() 

Cela permet à votre travailleur les threads finissent ce qu'ils veulent faire au lieu de simplement avoir chaque thread de travail comme démon et de sortir au milieu de l'exécution. Vous pouvez également faire le nettoyage que vous voulez.

Notez que cet exemple n'utilise pas q.join() - cela rend les choses plus complexes, bien que vous puissiez toujours l'utiliser. Si vous faites alors votre meilleur pari est d'utiliser des gestionnaires de signal au lieu d'exceptions pour détecter KeyboardInterrupt s. Par exemple:

from signal import signal, SIGINT 
def stop(signum, frame): 
    stopped.set() 
signal(SIGINT, stop) 

Cela vous permet de définir ce qui se passe lorsque vous appuyez sur Ctrl + C sans affecter quel que soit votre boucle principale est au milieu de. Donc, vous pouvez continuer à faire q.join() sans vous soucier d'être interrompu par un Ctrl + C. Bien sûr, avec mes exemples ci-dessus, vous n'avez pas besoin de vous joindre, mais vous pourriez avoir une autre raison de le faire.

+1

Donc, en gros, l'utilisation de q.join() rend difficile la gestion des exceptions dans les threads? –

+1

Ne devrait-il pas lire "signal (SIGINT, stop)"? – Ber

+0

Cela rend les choses plus complexes, mais j'ai ajouté un exemple avec des signaux pour vous montrer comment vous utiliseriez q.join() si vous aviez une bonne raison de l'utiliser. –

1

A (éventuellement) Note offtopic:

(...) 
for item in range(1, 10000): 
    q.put(item) 
(...) 

Vous pouvez utiliser xrange au lieu de gamme ici (sauf si vous utilisez python3000). Vous économiserez du CPU et de la mémoire en le faisant. Plus sur xrange peut être trouvé here.

Questions connexes