Warum wird sys.exit () nicht beendet, wenn es in einem Thread in Python aufgerufen wird?


98

Dies könnte eine dumme Frage sein, aber ich teste einige meiner Annahmen über Python und bin verwirrt darüber, warum das folgende Codefragment beim Aufruf im Thread nicht beendet wird, sondern beim Aufruf im Hauptthread beendet wird.

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"

In den Dokumenten für sys.exit () wird angegeben, dass der Aufruf von Python beendet werden soll. Ich kann der Ausgabe dieses Programms entnehmen, dass "Post-Thread-Exit" nie gedruckt wird, aber der Haupt-Thread läuft einfach weiter, selbst nachdem der Thread Exit aufgerufen hat.

Wird für jeden Thread eine separate Instanz des Interpreters erstellt, und der Aufruf von exit () beendet nur diese separate Instanz? Wenn ja, wie verwaltet die Threading-Implementierung den Zugriff auf freigegebene Ressourcen? Was wäre, wenn ich das Programm aus dem Thread beenden wollte (nicht, dass ich es wirklich möchte, aber nur damit ich es verstehe)?

Antworten:


71

sys.exit()löst die SystemExitAusnahme aus, ebenso wie thread.exit(). Wenn sys.exit()diese Ausnahme in diesem Thread ausgelöst wird, hat dies den gleichen Effekt wie das Aufrufen thread.exit(), weshalb nur der Thread beendet wird.


23

Was ist, wenn ich das Programm aus dem Thread beenden wollte?

Abgesehen von der von Deestan beschriebenen Methode können Sie aufrufen os._exit(beachten Sie den Unterstrich). Stellen Sie vor der Verwendung sicher, dass Sie verstehen, dass keine Bereinigungen durchgeführt werden (z. B. Aufrufen __del__oder ähnliches).


2
Wird es E / A spülen?
Lorenzo Belli

1
os._exit (n): "Beenden Sie den Prozess mit dem Status n, ohne Bereinigungshandler aufzurufen, Standardpuffer zu leeren usw."
Tim Richardson

Beachten Sie, dass bei os._exitVerwendung innerhalb von Flüchen die Konsole damit nicht auf einen normalen Zustand zurückgesetzt wird. Sie müssen resetin der Unix-Shell ausführen , um dies zu beheben.
sjngm

22

Was wäre, wenn ich das Programm aus dem Thread beenden wollte (nicht, dass ich es wirklich möchte, aber nur damit ich es verstehe)?

Zumindest unter Linux können Sie Folgendes tun:

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

Dies sendet a SIGINTan den Haupt-Thread, der a auslöst KeyboardInterrupt. Damit haben Sie eine ordentliche Bereinigung. Sie können das Signal sogar verarbeiten, wenn Sie dies benötigen.

Übrigens: Unter Windows können Sie nur ein SIGTERMSignal senden , das von Python nicht abgefangen werden kann. In diesem Fall können Sie einfach os._exitmit dem gleichen Effekt verwenden.


1
Funktioniert auch mit Flüchen im Gegensatz zu os._exit
sjngm

12

Ist die Tatsache, dass "Pre-Main-Exit, Post-Thread-Exit" gedruckt wird, was Sie stört?

Im Gegensatz zu einigen anderen Sprachen (wie Java), in denen das Analoge zu sys.exit( System.exitim Fall von Java) dazu führt, dass die VM / der Prozess / der Interpreter sofort gestoppt wird, löst Python sys.exitnur eine Ausnahme aus: insbesondere eine SystemExit-Ausnahme.

Hier sind die Dokumente für sys.exit(nur print sys.exit.__doc__):

Beenden Sie den Interpreter, indem Sie SystemExit (Status) erhöhen.
Wenn der Status weggelassen wird oder Keine, wird standardmäßig Null verwendet (dh Erfolg).
Wenn der Status numerisch ist, wird er als System-Exit-Status verwendet.
Wenn es sich um eine andere Art von Objekt handelt, wird es gedruckt und der System-
Exit-Status ist eins (dh Fehler).

Dies hat einige Konsequenzen:

  • In einem Thread wird nur der aktuelle Thread beendet, nicht der gesamte Prozess (vorausgesetzt, er gelangt bis an die Spitze des Stapels ...)
  • Objektdestruktoren ( __del__) werden möglicherweise aufgerufen, wenn die Stapelrahmen, die auf diese Objekte verweisen, abgewickelt werden
  • Schließlich werden Blöcke ausgeführt, während sich der Stapel abwickelt
  • Sie können eine SystemExitAusnahme abfangen

Der letzte ist möglicherweise der überraschendste und ein weiterer Grund, warum Sie fast nie eine uneingeschränkte exceptAussage in Ihrem Python-Code haben sollten.


11

Was wäre, wenn ich das Programm aus dem Thread beenden wollte (nicht, dass ich es wirklich möchte, aber nur damit ich es verstehe)?

Meine bevorzugte Methode ist das Erlang-ish-Message-Passing. Leicht vereinfacht mache ich das so:

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

3
Du brauchst kein Queuehier. Nur ein einfacher reicht aus bool. Der klassische Name für diese Variable lautet is_activeund der anfängliche Standardwert lautet True.
Acumenus

3
Ja du hast Recht. Laut effbot.org/zone/thread-synchronization.htm ist das Ändern einer bool(oder einer anderen atomaren Operation) für dieses spezielle Problem perfekt geeignet. Der Grund , warum ich mit gehen Queues ist , dass , wenn sie mit Gewindemitteln arbeiten Ich neige dazu , verschiedene Signale , um am Ende benötigen ( flush, reconnect, exit, etc ...) fast sofort.
Deestan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.