107 votes

Pourquoi sys.exit() ne se termine-t-il pas lorsqu'il est appelé dans un thread en Python ?

C'est peut-être une question stupide, mais je teste certaines de mes hypothèses sur Python et je ne comprends pas pourquoi le bout de code suivant ne se termine pas lorsqu'il est appelé dans le thread, mais se termine lorsqu'il est appelé dans le thread principal.

import sys, time
from threading import Thread

def testexit():
    time.sleep(5)
    sys.exit()
    print "post thread exit"

t = Thread(target = testexit)
t.start()
t.join()
print "pre main exit, post thread exit"
sys.exit()
print "post main exit"

La documentation de sys.exit() indique que l'appel doit sortir de Python. Je peux voir dans la sortie de ce programme que "post thread exit" n'est jamais imprimé, mais le thread principal continue à fonctionner même après que le thread ait appelé exit.

Est-ce qu'une instance séparée de l'interpréteur est créée pour chaque thread, et l'appel à exit() ne fait que quitter cette instance séparée ? Dans ce cas, comment l'implémentation du threading gère-t-elle l'accès aux ressources partagées ? Que se passe-t-il si je veux quitter le programme à partir du thread (non pas que je le veuille vraiment, mais je veux juste comprendre) ?

82voto

rpkelly Points 977

sys.exit() soulève le SystemExit ainsi que l'exception thread.exit() . Ainsi, lorsque sys.exit() lève cette exception dans ce thread, cela a le même effet que d'appeler thread.exit() c'est pourquoi seul le fil de discussion sort.

28voto

Chris Points 440

Et si je voulais quitter le programme à partir du fil de discussion ?

Pour Linux :

os.kill(os.getpid(), signal.SIGINT)

Cela envoie un SIGINT au thread principal qui lève un KeyboardInterrupt . Avec cela, vous avez un nettoyage approprié. Vous pouvez également enregistrer un gestionnaire, si vous souhaitez réagir différemment.

Ce qui précède ne fonctionne pas sous Windows, car vous ne pouvez envoyer qu'un message de type SIGTERM qui n'est pas géré par Python et qui a le même effet que le signal os._exit() .

Pour Windows :

Vous pouvez utiliser :

os._exit()

Cela mettra fin à l'ensemble du processus sans aucun nettoyage. Si vous avez besoin d'un nettoyage, vous devez communiquer avec le thread principal d'une autre manière.

2 votes

Fonctionne également avec les curses, contrairement à os._exit

1 votes

module 'sys' has no attribute '_exit' . Windows 10, Python 3.7.9

0 votes

Vous avez raison, ça devrait être os._exit . Maintenant, je l'ai changé.

25voto

Helmut Grohne Points 1452

Et si je voulais quitter le programme depuis le fil de discussion ?

En dehors de la méthode décrite par Deestan, vous pouvez appeler os._exit (remarquez le trait de soulignement). Avant de l'utiliser, assurez-vous de bien comprendre qu'il fait pas de les nettoyages (comme appeler __del__ ou similaire).

2 votes

Est-ce qu'il y aura une chasse d'eau à l'entrée et à la sortie ?

2 votes

Os._exit(n) : "Quitter le processus avec le statut n, sans appeler les gestionnaires de nettoyage, vider les tampons stdio, etc."

0 votes

Notez que lorsque os._exit est utilisé dans les curses, la console n'est pas réinitialisée à un état normal avec cela. Vous devez exécuter reset dans le shell Unix pour résoudre ce problème.

13voto

Deestan Points 7298

Et si je voulais quitter le programme du fil (non pas que je veuille réellement mais juste pour que je comprenne) ?

Ma méthode préférée est le passage de messages à la Erlang. Légèrement simplifié, je le fais comme ceci :

import sys, time
import threading
import Queue # thread-safe

class CleanExit:
  pass

ipq = Queue.Queue()

def testexit(ipq):
  time.sleep(5)
  ipq.put(CleanExit)
  return

threading.Thread(target=testexit, args=(ipq,)).start()
while True:
  print "Working..."
  time.sleep(1)
  try:
    if ipq.get_nowait() == CleanExit:
      sys.exit()
  except Queue.Empty:
    pass

4 votes

Vous n'avez pas besoin d'un Queue ici. Juste un simple bool Le nom classique de cette variable est is_active et sa valeur initiale par défaut est True .

4 votes

Oui, vous avez raison. Selon effbot.org/zone/thread-synchronization.htm en modifiant une bool (ou toute autre opération atomique) fera parfaitement l'affaire pour ce problème particulier. La raison pour laquelle j'opte pour Queue est que lorsque je travaille avec des agents filaires, j'ai tendance à avoir besoin de plusieurs signaux différents ( flush , reconnect , exit etc...) presque immédiatement.

1 votes

Deestan : Depuis un bool fonctionnerait, comme l'a souligné @Acumenus, de même qu'une simple int semble être tout ce qui est nécessaire pour pouvoir gérer plusieurs signaux différents - bool est juste une sous-classe de int après tout.

12voto

Laurence Gonsalves Points 50783

C'est le fait que "pre main exit, post thread exit" soit imprimé qui vous dérange ?

Contrairement à certains autres langages (comme Java) où l'analogue de sys.exit ( System.exit dans le cas de Java) entraîne l'arrêt immédiat de la VM/du processus/de l'interpréteur, tandis que l'option de Python sys.exit ne fait que lancer une exception : une exception SystemExit en particulier.

Voici les documents pour sys.exit (juste print sys.exit.__doc__ ):

Quitter l'interpréteur en levant SystemExit(status).
Si l'état est omis ou nul, il prend la valeur zéro par défaut (c'est-à-dire le succès).
Si l'état est numérique, il sera utilisé comme état de sortie du système.
S'il s'agit d'un autre type d'objet, il sera imprimé et le système
l'état de sortie sera de un (c'est-à-dire un échec).

Cela a quelques conséquences :

  • dans un thread, il ne tue que le thread en cours, pas le processus entier (en supposant qu'il arrive jusqu'au sommet de la pile...).
  • les destructeurs d'objets ( __del__ ) sont potentiellement invoqués au fur et à mesure que les cadres de la pile qui font référence à ces objets sont déroulés.
  • les blocs finaux sont exécutés lorsque la pile se déroule
  • vous pouvez attraper un SystemExit exception

La dernière est peut-être la plus surprenante, et c'est une raison de plus pour laquelle vous ne devriez presque jamais faire appel à une personne non qualifiée. except dans votre code Python.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X