logo

Python | Verschiedene Möglichkeiten, einen Thread zu beenden

Im Allgemeinen gilt das abrupte Beenden von Threads als schlechte Programmierpraxis. Das abrupte Beenden eines Threads kann dazu führen, dass eine kritische Ressource, die ordnungsgemäß geschlossen und geöffnet werden muss, zurückbleibt. Möglicherweise möchten Sie einen Thread jedoch beenden, sobald eine bestimmte Zeitspanne verstrichen ist oder ein Interrupt generiert wurde. Es gibt verschiedene Methoden, mit denen Sie einen Thread in Python beenden können.

  • Ausnahmen in einem Python-Thread auslösen
  • Stoppflag setzen/zurücksetzen
  • Verwendung von Traces zum Beenden von Threads
  • Verwenden des Multiprocessing-Moduls zum Beenden von Threads
  • Beende den Python-Thread, indem du ihn als Daemon festlegst
  • Verwenden einer versteckten Funktion _stop()

Ausnahmen in einem Python-Thread auslösen:
Diese Methode verwendet die Funktion PyThreadState_SetAsyncExc() um eine Ausnahme in einem Thread auszulösen. Zum Beispiel,



Python3








# Python program raising> # exceptions in a python> # thread> import> threading> import> ctypes> import> time> > class> thread_with_exception(threading.Thread):> >def> __init__(>self>, name):> >threading.Thread.__init__(>self>)> >self>.name>=> name> > >def> run(>self>):> ># target function of the thread class> >try>:> >while> True>:> >print>(>'running '> +> self>.name)> >finally>:> >print>(>'ended'>)> > >def> get_id(>self>):> ># returns id of the respective thread> >if> hasattr>(>self>,>'_thread_id'>):> >return> self>._thread_id> >for> id>, thread>in> threading._active.items():> >if> thread>is> self>:> >return> id> > >def> raise_exception(>self>):> >thread_id>=> self>.get_id()> >res>=> ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,> >ctypes.py_object(SystemExit))> >if> res>>1>:> >ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,>0>)> >print>(>'Exception raise failure'>)> > t1>=> thread_with_exception(>'Thread 1'>)> t1.start()> time.sleep(>2>)> t1.raise_exception()> t1.join()>

>

>

Wenn wir den obigen Code auf einer Maschine ausführen, werden Sie feststellen, dass die Zielfunktion run() endet, sobald die Funktion raise_Exception() aufgerufen wird. Denn sobald eine Ausnahme auftritt, springt die Programmsteuerung aus dem Try-Block und die Funktion run() wird beendet. Danach kann die Funktion „join()“ aufgerufen werden, um den Thread zu beenden. Wenn die Funktion run_Exception() nicht vorhanden ist, läuft die Zielfunktion run() ewig weiter und die Funktion join() wird nie aufgerufen, um den Thread zu beenden.

Stoppflag setzen/zurücksetzen:
Um einen Thread zu beenden, können wir ein Stop-Flag deklarieren und dieses Flag wird gelegentlich vom Thread überprüft. Zum Beispiel

Python3




# Python program showing> # how to kill threads> # using set/reset stop> # flag> import> threading> import> time> def> run():> >while> True>:> >print>(>'thread running'>)> >global> stop_threads> >if> stop_threads:> >break> stop_threads>=> False> t1>=> threading.Thread(target>=> run)> t1.start()> time.sleep(>1>)> stop_threads>=> True> t1.join()> print>(>'thread killed'>)>

>

>

Sobald im obigen Code die globale Variable stop_threads gesetzt ist, endet die Zielfunktion run() und der Thread t1 kann mit t1.join() beendet werden. Aus bestimmten Gründen kann man jedoch auf die Verwendung globaler Variablen verzichten. In solchen Situationen können Funktionsobjekte übergeben werden, um eine ähnliche Funktionalität wie unten gezeigt bereitzustellen.

Python3




# Python program killing> # threads using stop> # flag> import> threading> import> time> def> run(stop):> >while> True>:> >print>(>'thread running'>)> >if> stop():> >break> > def> main():> >stop_threads>=> False> >t1>=> threading.Thread(target>=> run, args>=>(>lambda> : stop_threads, ))> >t1.start()> >time.sleep(>1>)> >stop_threads>=> True> >t1.join()> >print>(>'thread killed'>)> main()>

>

>

Das im obigen Code übergebene Funktionsobjekt gibt immer den Wert der lokalen Variablen stop_threads zurück. Dieser Wert wird in der Funktion run() überprüft und sobald stop_threads zurückgesetzt wird, endet die Funktion run() und der Thread kann beendet werden.

Verwendung von Traces zum Beenden von Threads:
Diese Methode funktioniert durch Installation Spuren in jedem Thread. Jeder Trace beendet sich selbst, wenn ein Stimulus oder ein Flag erkannt wird, wodurch der zugehörige Thread sofort beendet wird. Zum Beispiel

Python3




# Python program using> # traces to kill threads> import> sys> import> trace> import> threading> import> time> class> thread_with_trace(threading.Thread):> >def> __init__(>self>,>*>args,>*>*>keywords):> >threading.Thread.__init__(>self>,>*>args,>*>*>keywords)> >self>.killed>=> False> >def> start(>self>):> >self>.__run_backup>=> self>.run> >self>.run>=> self>.__run> >threading.Thread.start(>self>)> >def> __run(>self>):> >sys.settrace(>self>.globaltrace)> >self>.__run_backup()> >self>.run>=> self>.__run_backup> >def> globaltrace(>self>, frame, event, arg):> >if> event>=>=> 'call'>:> >return> self>.localtrace> >else>:> >return> None> >def> localtrace(>self>, frame, event, arg):> >if> self>.killed:> >if> event>=>=> 'line'>:> >raise> SystemExit()> >return> self>.localtrace> >def> kill(>self>):> >self>.killed>=> True> def> func():> >while> True>:> >print>(>'thread running'>)> t1>=> thread_with_trace(target>=> func)> t1.start()> time.sleep(>2>)> t1.kill()> t1.join()> if> not> t1.isAlive():> >print>(>'thread killed'>)>

>

>

In diesem Code wird start() leicht modifiziert, um die System-Trace-Funktion mit festzulegen settrace() . Die lokale Trace-Funktion ist so definiert, dass immer dann, wenn das Kill-Flag (getötet) des jeweiligen Threads gesetzt ist, bei der Ausführung der nächsten Codezeile eine SystemExit-Ausnahme ausgelöst wird, die die Ausführung der Zielfunktion func beendet. Jetzt kann der Thread mit join() beendet werden.

Verwenden des Multiprocessing-Moduls zum Beenden von Threads:
Mit dem Multiprocessing-Modul von Python können Sie Prozesse auf ähnliche Weise erzeugen, wie Sie Threads mit dem Threading-Modul erzeugen. Die Schnittstelle des Multithreading-Moduls ähnelt der des Threading-Moduls. Beispielsweise haben wir in einem bestimmten Code drei Threads (Prozesse) erstellt, die von 1 bis 9 zählen.

Python3

wie man ein Skript ausführt




# Python program creating> # three threads> import> threading> import> time> # counts from 1 to 9> def> func(number):> >for> i>in> range>(>1>,>10>):> >time.sleep(>0.01>)> >print>(>'Thread '> +> str>(number)>+> ': prints '> +> str>(number>*>i))> # creates 3 threads> for> i>in> range>(>0>,>3>):> >thread>=> threading.Thread(target>=>func, args>=>(i,))> >thread.start()>

>

>

Die Funktionalität des obigen Codes kann mit sehr wenigen Änderungen auch durch die Verwendung des Multiprocessing-Moduls auf ähnliche Weise implementiert werden. Siehe den unten angegebenen Code.

Python3




# Python program creating> # thread using multiprocessing> # module> import> multiprocessing> import> time> def> func(number):> >for> i>in> range>(>1>,>10>):> >time.sleep(>0.01>)> >print>(>'Processing '> +> str>(number)>+> ': prints '> +> str>(number>*>i))> for> i>in> range>(>0>,>3>):> >process>=> multiprocessing.Process(target>=>func, args>=>(i,))> >process.start()>

>

>

Obwohl die Schnittstelle der beiden Module ähnlich ist, weisen die beiden Module sehr unterschiedliche Implementierungen auf. Alle Threads teilen globale Variablen, während Prozesse vollständig voneinander getrennt sind. Daher ist das Beenden von Prozessen im Vergleich zum Beenden von Threads viel sicherer. Der Process-Klasse wird eine Methode zur Verfügung gestellt, beenden() , um einen Prozess abzubrechen. Kommen wir nun zurück zum ursprünglichen Problem. Angenommen, wir möchten im obigen Code alle Prozesse nach Ablauf von 0,03 Sekunden beenden. Diese Funktionalität wird mithilfe des Multiprocessing-Moduls im folgenden Code erreicht.

Python3




# Python program killing> # a thread using multiprocessing> # module> import> multiprocessing> import> time> def> func(number):> >for> i>in> range>(>1>,>10>):> >time.sleep(>0.01>)> >print>(>'Processing '> +> str>(number)>+> ': prints '> +> str>(number>*>i))> # list of all processes, so that they can be killed afterwards> all_processes>=> []> for> i>in> range>(>0>,>3>):> >process>=> multiprocessing.Process(target>=>func, args>=>(i,))> >process.start()> >all_processes.append(process)> # kill all processes after 0.03s> time.sleep(>0.03>)> for> process>in> all_processes:> >process.terminate()>

>

>

Allerdings haben die beiden Module unterschiedliche Implementierungen. Diese vom Multiprocessing-Modul im obigen Code bereitgestellte Funktionalität ähnelt dem Beenden von Threads. Daher kann das Multiprocessing-Modul einfach verwendet werden Alternative wann immer wir das Beenden von Threads in Python implementieren müssen.

Beenden des Python-Threads durch Festlegen als Daemon:
Daemon-Threads sind die Threads, die beendet werden, wenn das Hauptprogramm beendet wird. Zum Beispiel

Python3




import> threading> import> time> import> sys> def> func():> >while> True>:> >time.sleep(>0.5>)> >print>(>'Thread alive, and it won't die on program termination'>)> t1>=> threading.Thread(target>=>func)> t1.start()> time.sleep(>2>)> sys.exit()>

>

>

Beachten Sie, dass Thread t1 am Leben bleibt und verhindert, dass das Hauptprogramm über sys.exit() beendet wird. In Python blockiert jeder aktive Nicht-Daemon-Thread das Beenden des Hauptprogramms. Daemon-Threads selbst werden beendet, sobald das Hauptprogramm beendet wird. Mit anderen Worten: Sobald das Hauptprogramm beendet wird, werden alle Daemon-Threads beendet. Um einen Thread als Daemon zu deklarieren, setzen wir das Schlüsselwortargument „daemon“ auf „True“. Im angegebenen Code wird beispielsweise die Eigenschaft von Daemon-Threads demonstriert.

Python3




# Python program killing> # thread using daemon> import> threading> import> time> import> sys> def> func():> >while> True>:> >time.sleep(>0.5>)> >print>(>'Thread alive, but it will die on program termination'>)> t1>=> threading.Thread(target>=>func)> t1.daemon>=> True> t1.start()> time.sleep(>2>)> sys.exit()>

>

>

Beachten Sie, dass der Thread t1 beendet wird, sobald das Hauptprogramm beendet wird. Diese Methode erweist sich in Fällen als äußerst nützlich, in denen die Programmbeendigung dazu verwendet werden kann, das Beenden von Threads auszulösen. Beachten Sie, dass in Python das Hauptprogramm beendet wird, sobald alle Nicht-Daemon-Threads tot sind, unabhängig von der Anzahl der aktiven Daemon-Threads. Daher werden die von diesen Daemon-Threads gehaltenen Ressourcen wie offene Dateien, Datenbanktransaktionen usw. möglicherweise nicht ordnungsgemäß freigegeben. Der anfängliche Steuerungsthread in einem Python-Programm ist kein Daemon-Thread. Das gewaltsame Beenden eines Threads wird nicht empfohlen, es sei denn, es ist sicher, dass dadurch keine Lecks oder Deadlocks verursacht werden.
Verwenden einer versteckten Funktion _stop() :
Um einen Thread zu beenden, verwenden wir die versteckte Funktion _stop(). Diese Funktion ist nicht dokumentiert, könnte aber in der nächsten Python-Version verschwinden.

Python3




# Python program killing> # a thread using ._stop()> # function> import> time> import> threading> class> MyThread(threading.Thread):> ># Thread class with a _stop() method.> ># The thread itself has to check> ># regularly for the stopped() condition.> >def> __init__(>self>,>*>args,>*>*>kwargs):> >super>(MyThread,>self>).__init__(>*>args,>*>*>kwargs)> >self>._stop>=> threading.Event()> ># function using _stop function> >def> stop(>self>):> >self>._stop.>set>()> >def> stopped(>self>):> >return> self>._stop.isSet()> >def> run(>self>):> >while> True>:> >if> self>.stopped():> >return> >print>(>'Hello, world!'>)> >time.sleep(>1>)> t1>=> MyThread()> t1.start()> time.sleep(>5>)> t1.stop()> t1.join()>

>

Java und Swing

>

Notiz: Die oben genannten Methoden funktionieren in der einen oder anderen Situation möglicherweise nicht, da Python keine direkte Methode zum Beenden von Threads bereitstellt.