Wie beende ich die gesamte Anwendung aus einem Python-Thread?

73

Wie kann ich meine gesamte Python-Anwendung aus einem ihrer Threads beenden? sys.exit()Beendet nur den Thread, in dem er aufgerufen wird, so dass dies keine Hilfe ist.

Ich möchte keine os.kill()Lösung verwenden, da diese nicht sehr sauber ist.

linkmaster03
quelle

Antworten:

60

Wenn alle Ihre Threads außer den Haupt-Threads Dämonen sind, ist der beste Ansatz im Allgemeinen thread.interrupt_main () - jeder Thread kann ihn verwenden, um ein KeyboardInterruptim Haupt-Thread zu erhöhen , was normalerweise zu einem einigermaßen sauberen Austritt aus dem Haupt-Thread führen kann (einschließlich Finalizer im Haupt-Thread werden aufgerufen, etc).

Wenn dies dazu führt, dass ein Nicht-Daemon-Thread den gesamten Prozess am Leben erhält, müssen Sie natürlich nachverfolgen, os._exitwie Mark es empfiehlt - aber ich würde das als letzten Ausweg betrachten (irgendwie wie ein kill -9;-), weil es die Dinge beendet ziemlich brüsk (Finalizer laufen nicht, einschließlich try/finallyBlöcke, withBlöcke, atexitFunktionen usw.).

Alex Martelli
quelle
63

Kurze Antwort: verwenden os._exit.

Lange Antwort mit Beispiel:

Ich habe ein einfaches Threading-Beispiel aus einem Tutorial zu DevShed gezogen und leicht modifiziert :

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()

Wenn Sie auskommentiert bleiben sys.exit(1), stirbt das Skript, nachdem der dritte Thread ausgedruckt wurde. Wenn Sie verwenden sys.exit(1)und auskommentieren, os._exit(1)wird der dritte Thread nicht gedruckt (done)und das Programm durchläuft alle sieben Threads.

os._exit"sollte normalerweise nur im untergeordneten Prozess nach einem fork () verwendet werden" - und ein separater Thread ist für Ihren Zweck nah genug daran. Beachten Sie auch, dass direkt danach os._exitauf dieser Handbuchseite mehrere Aufzählungswerte aufgeführt sind , und Sie sollten diese als Argumente os._exitanstelle einfacher Zahlen bevorzugen, wie ich sie im obigen Beispiel verwendet habe.

Mark Rushakoff
quelle
1
Dies funktioniert besonders gut, wenn Sie in einem Docker-Container ausgeführt werden. Das Problem in einem Docker-Container ist, dass wir pid 1 nicht töten können. Die Verwendung von os._exit (1) hat funktioniert.
Ajaali
21

thread.interrupt_main()In manchen Situationen kann die Verwendung nicht helfen. KeyboardInterrupts werden häufig in Befehlszeilenanwendungen verwendet, um den aktuellen Befehl zu beenden oder die Eingabezeile zu bereinigen.

Außerdem os._exitwird der Prozess sofort abgebrochen, ohne dass finallyBlöcke in Ihrem Code ausgeführt werden, was gefährlich sein kann (Dateien und Verbindungen werden beispielsweise nicht geschlossen).

Die Lösung, die ich gefunden habe, besteht darin, einen Signalhandler im Hauptthread zu registrieren, der eine benutzerdefinierte Ausnahme auslöst. Verwenden Sie den Hintergrund-Thread, um das Signal auszulösen.

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')

Verwandte Fragen:

Nagasaki45
quelle
2
Dies funktioniert nicht unter WindowsAttributeError: module 'signal' has no attribute 'SIGUSR1'
pbn
Gibt es eine Alternative dazu für Windows?
MasayoMusic
1

Der einfachste Weg, das gesamte Programm zu beenden, besteht darin, das Programm mithilfe der Prozess-ID (pid) zu beenden.

import os
import psutil

current_system_pid = os.getpid()

ThisSystem = psutil.Process(current_system_pid)
ThisSystem.terminate()

So installieren Sie psutl: - "pip install psutil"

GR Tech School
quelle