web-dev-qa-db-fra.com

Comment quitter l'application entière d'un thread Python?

Comment puis-je quitter l'intégralité de mon application Python à partir de l'un de ses threads? Sys.exit () ne termine que le thread dans lequel elle est appelée, donc cela ne sert à rien.

Je ne voudrais pas utiliser une solution os.kill (), car ce n'est pas très propre.

55
linkmaster03

Si tous vos threads sauf les principaux sont des démons, la meilleure approche est généralement thread.interrupt_main () - n'importe quel thread peut l'utiliser pour augmenter un KeyboardInterrupt dans le thread principal, ce qui peut normalement conduire à une sortie raisonnablement propre du thread principal (y compris les finaliseurs du thread principal appelés, etc.).

Bien sûr, si cela se traduit par un thread non démon qui maintient l'ensemble du processus en vie, vous devez effectuer un suivi avec os._exit comme le recommande Mark - mais je verrais cela en dernier recours (un peu comme un kill -9 ;-) car il termine les choses assez brusquement (les finaliseurs ne sont pas exécutés, y compris try/finally blocs, with blocs, atexit fonctions, etc.).

50
Alex Martelli

Réponse courte: utilisez os._exit .

Réponse longue avec exemple:

J'ai tiré et légèrement modifié un exemple de filetage simple de n tutoriel sur DevShed :

import threading, sys, os

theVar = 1

class MyThread ( threading.Thread ):

   def run ( self ):

      global theVar
      print 'This is thread ' + str ( theVar ) + ' speaking.'
      print 'Hello and good bye.'
      theVar = theVar + 1
      if theVar == 4:
          #sys.exit(1)
          os._exit(1)
      print '(done)'

for x in xrange ( 7 ):
   MyThread().start()

Si vous gardez sys.exit(1) mis en commentaire, le script mourra après l'impression du troisième thread. Si vous utilisez sys.exit(1) et commentez os._exit(1), le troisième thread n'imprime pas (done) et le programme parcourt les sept threads.

os._exit "Ne devrait normalement être utilisé dans le processus enfant qu'après un fork ()" - et un thread séparé est suffisamment proche de celui pour votre usage. Notez également qu'il existe plusieurs valeurs énumérées juste après os._exit Dans cette page de manuel, et vous devriez préférer celles-ci comme arguments à os._exit Au lieu de simples chiffres comme je l'ai utilisé dans l'exemple ci-dessus.

45
Mark Rushakoff

L'utilisation de thread.interrupt_main() peut ne pas aider dans certaines situations. KeyboardInterrupts sont souvent utilisés dans les applications de ligne de commande pour quitter la commande en cours ou pour nettoyer la ligne d'entrée.

En plus, os._exit va tuer le processus immédiatement sans exécuter de bloc finally dans votre code, ce qui peut être dangereux (les fichiers et les connexions ne seront pas fermés par exemple).

La solution que j'ai trouvée consiste à enregistrer un gestionnaire de signal dans le thread principal qui déclenche une exception personnalisée. Utilisez le fil d'arrière-plan pour déclencher le signal.

import signal
import os
import threading
import time


class ExitCommand(Exception):
    pass


def signal_handler(signal, frame):
    raise ExitCommand()


def thread_job():
    time.sleep(5)
    os.kill(os.getpid(), signal.SIGUSR1)


signal.signal(signal.SIGUSR1, signal_handler)
threading.Thread(target=thread_job).start()  # thread will fire in 5 seconds
try:
    while True:
        user_input = raw_input('Blocked by raw_input loop ')
        # do something with 'user_input'
except ExitCommand:
    pass
finally:
    print('finally will still run')

Questions connexes:

18
Nagasaki45