Ist es möglich, einen laufenden Thread zu beenden, ohne Flags / Semaphoren / etc. Zu setzen / zu überprüfen?
quelle
Ist es möglich, einen laufenden Thread zu beenden, ohne Flags / Semaphoren / etc. Zu setzen / zu überprüfen?
Es ist im Allgemeinen ein schlechtes Muster, einen Thread in Python und in jeder Sprache abrupt zu beenden. Denken Sie an folgende Fälle:
Die gute Möglichkeit, dies zu handhaben, wenn Sie es sich leisten können (wenn Sie Ihre eigenen Threads verwalten), besteht darin, ein exit_request-Flag zu haben, das jeder Thread in regelmäßigen Abständen überprüft, um festzustellen, ob es Zeit für das Beenden ist.
Zum Beispiel:
import threading
class StoppableThread(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(StoppableThread, self).__init__(*args, **kwargs)
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
In diesem Code sollten Sie stop()
den Thread aufrufen, wenn er beendet werden soll, und warten, bis der Thread ordnungsgemäß beendet wurde join()
. Der Thread sollte in regelmäßigen Abständen das Stopp-Flag überprüfen.
Es gibt jedoch Fälle, in denen Sie einen Thread wirklich beenden müssen. Ein Beispiel ist, wenn Sie eine externe Bibliothek umschließen, die für lange Anrufe belegt ist, und diese unterbrechen möchten.
Mit dem folgenden Code kann (mit einigen Einschränkungen) eine Ausnahme in einem Python-Thread ausgelöst werden:
def _async_raise(tid, exctype):
'''Raises an exception in the threads with id tid'''
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# "if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
raise SystemError("PyThreadState_SetAsyncExc failed")
class ThreadWithExc(threading.Thread):
'''A thread class that supports raising exception in the thread from
another thread.
'''
def _get_my_tid(self):
"""determines this (self's) thread id
CAREFUL : this function is executed in the context of the caller
thread, to get the identity of the thread represented by this
instance.
"""
if not self.isAlive():
raise threading.ThreadError("the thread is not active")
# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id
# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid
# TODO: in python 2.6, there's a simpler way to do : self.ident
raise AssertionError("could not determine the thread's id")
def raiseExc(self, exctype):
"""Raises the given exception type in the context of this thread.
If the thread is busy in a system call (time.sleep(),
socket.accept(), ...), the exception is simply ignored.
If you are sure that your exception should terminate the thread,
one way to ensure that it works is:
t = ThreadWithExc( ... )
...
t.raiseExc( SomeException )
while t.isAlive():
time.sleep( 0.1 )
t.raiseExc( SomeException )
If the exception is to be caught by the thread, you need a way to
check that your thread has caught it.
CAREFUL : this function is executed in the context of the
caller thread, to raise an excpetion in the context of the
thread represented by this instance.
"""
_async_raise( self._get_my_tid(), exctype )
(Basierend auf Killable Threads von Tomer Filiba. Das Zitat über den Rückgabewert von PyThreadState_SetAsyncExc
scheint aus einer alten Version von Python zu stammen .)
Wie in der Dokumentation erwähnt, handelt es sich hierbei nicht um ein Wundermittel, da der Thread die Unterbrechung nicht abfängt, wenn er außerhalb des Python-Interpreters beschäftigt ist.
Ein gutes Verwendungsmuster dieses Codes besteht darin, dass der Thread eine bestimmte Ausnahme abfängt und die Bereinigung durchführt. Auf diese Weise können Sie eine Aufgabe unterbrechen und trotzdem eine ordnungsgemäße Bereinigung durchführen.
SO_REUSEADDR
Socket-Option verwenden, umAddress already in use
Fehler zu vermeiden .None
statt0
für denres != 1
Fall übergeben, und ich musste das aufrufenctypes.c_long(tid)
und an eine beliebige ctypes-Funktion übergeben, anstatt direkt an die tid.Es gibt keine offizielle API dafür, nein.
Sie müssen die Plattform-API verwenden, um den Thread zu beenden, z. B. pthread_kill oder TerminateThread. Sie können auf eine solche API zugreifen, z. B. über Pythonwin oder über ctypes.
Beachten Sie, dass dies von Natur aus unsicher ist. Dies führt wahrscheinlich zu uneinbringlichem Müll (von lokalen Variablen der Stapelrahmen, die zu Müll werden) und kann zu Deadlocks führen, wenn der getötete Thread die GIL zu dem Zeitpunkt hat, an dem er getötet wird.
quelle
Eine
multiprocessing.Process
Dosep.terminate()
In den Fällen, in denen ich einen Thread beenden möchte, aber keine Flags / Sperren / Signale / Semaphoren / Ereignisse / was auch immer verwenden möchte, befördere ich die Threads zu vollständigen Prozessen. Für Code, der nur wenige Threads verwendet, ist der Overhead nicht so schlimm.
Dies ist beispielsweise praktisch, um Hilfsthreads, die blockierende E / A ausführen, einfach zu beenden
Die Konvertierung ist trivial: Ersetzen Sie im zugehörigen Code alle
threading.Thread
durchmultiprocessing.Process
und allequeue.Queue
durchmultiprocessing.Queue
und fügen Sie die erforderlichen Aufrufep.terminate()
Ihres übergeordneten Prozesses hinzu, der sein untergeordnetes Element töten möchtep
Siehe die Python-Dokumentation für
multiprocessing
.quelle
multiprocessing
ist nett, aber sei dir bewusst, dass Argumente für den neuen Prozess ausgewählt werden. Wenn eines der Argumente nicht auswählbar ist (wie alogging.log
), ist es möglicherweise keine gute Idee, es zu verwendenmultiprocessing
.multiprocessing
Argumente werden für den neuen Prozess unter Windows ausgewählt, aber Linux verwendet Forking, um sie zu kopieren (Python 3.7, unsicher, welche anderen Versionen). Sie erhalten also Code, der unter Linux funktioniert, unter Windows jedoch Pickle-Fehler verursacht.multiprocessing
mit der Protokollierung ist eine knifflige Angelegenheit. Muss verwendet werdenQueueHandler
(siehe dieses Tutorial ). Ich habe es auf die harte Tour gelernt.Wenn Sie versuchen, das gesamte Programm zu beenden, können Sie den Thread als "Daemon" festlegen. siehe Thread.daemon
quelle
Wie andere bereits erwähnt haben, besteht die Norm darin, ein Stopp-Flag zu setzen. Für etwas Leichtes (keine Unterklasse von Thread, keine globale Variable) ist ein Lambda-Rückruf eine Option. (Beachten Sie die Klammern in
if stop()
.)Das Ersetzen
print()
durch einepr()
Funktion, die immer spült (sys.stdout.flush()
), kann die Genauigkeit der Shell-Ausgabe verbessern.(Nur unter Windows / Eclipse / Python3.3 getestet)
quelle
pr()
Funktion?Dies basiert auf thread2 - tötbaren Threads (Python-Rezept)
Sie müssen PyThreadState_SetasyncExc () aufrufen, das nur über ctypes verfügbar ist.
Dies wurde nur unter Python 2.7.3 getestet, funktioniert jedoch wahrscheinlich mit anderen neueren 2.x-Versionen.
quelle
KeyboardInterrupt
damit sie eine Chance haben, aufzuräumen. Wenn sie danach NOCH hängen,SystemExit
ist dies angemessen, oder beenden Sie den Vorgang einfach von einem Terminal aus.pthread_cleanup_push()/_pop()
, wäre die korrekte Implementierung eine Menge Arbeit und würde den Interpreter merklich verlangsamen.Sie sollten einen Thread niemals gewaltsam töten, ohne mit ihm zusammenzuarbeiten.
Durch das Beenden eines Threads werden alle Garantien entfernt, dass try / finally-Blöcke eingerichtet wurden, sodass Sperren möglicherweise gesperrt, Dateien geöffnet usw. bleiben.
Das einzige Mal, wenn Sie argumentieren können, dass das gewaltsame Beenden von Threads eine gute Idee ist, ist das schnelle Beenden eines Programms, jedoch niemals einzelner Threads.
quelle
In Python können Sie einen Thread einfach nicht direkt beenden.
Wenn Sie NICHT wirklich einen Thread (!) Benötigen müssen, können Sie anstelle des Threading- Pakets das Multiprocessing- Paket verwenden . Um einen Prozess abzubrechen, können Sie hier einfach die Methode aufrufen:
Python beendet Ihren Prozess (unter Unix über das SIGTERM-Signal, unter Windows über den
TerminateProcess()
Aufruf). Achten Sie darauf, es zu verwenden, während Sie eine Warteschlange oder ein Rohr verwenden! (Es kann die Daten in der Warteschlange / Pipe beschädigen.)Beachten Sie, dass das
multiprocessing.Event
und dasmultiprocessing.Semaphore
genau so funktionieren wie dasthreading.Event
und dasthreading.Semaphore
. Tatsächlich sind die ersten Klone der letzteren.Wenn Sie WIRKLICH einen Thread verwenden müssen, gibt es keine Möglichkeit, ihn direkt zu beenden. Sie können jedoch einen "Daemon-Thread" verwenden . Tatsächlich kann in Python ein Thread als Daemon gekennzeichnet werden :
Das Hauptprogramm wird beendet, wenn keine aktiven Nicht-Daemon-Threads mehr vorhanden sind. Mit anderen Worten, wenn Ihr Hauptthread (der natürlich ein Nicht-Daemon-Thread ist) seine Operationen beendet, wird das Programm beendet, selbst wenn noch einige Daemon-Threads funktionieren.
Beachten Sie, dass ein Thread wie
daemon
vor demstart()
Aufruf der Methode festgelegt werden muss!Natürlich können und sollten Sie
daemon
auch mit verwendenmultiprocessing
. Wenn der Hauptprozess beendet wird, versucht er hier, alle seine dämonischen untergeordneten Prozesse zu beenden.Schließlich Bitte beachten Sie, dass
sys.exit()
undos.kill()
nicht Entscheidungen.quelle
Sie können einen Thread beenden, indem Sie einen Trace in den Thread installieren, der den Thread beendet. Eine mögliche Implementierung finden Sie im beigefügten Link.
Töte einen Thread in Python
quelle
Wenn Sie explizit
time.sleep()
als Teil Ihres Threads aufrufen (z. B. einen externen Dienst abfragen), besteht eine Verbesserung der Phillipe-Methode darin, das Timeout in der Methode vonevent
's zuwait()
verwenden, wo immer Sie sich befindensleep()
Zum Beispiel:
Dann, um es auszuführen
Der Vorteil der Verwendung
wait()
anstelle dersleep()
regelmäßigen Überprüfung des Ereignisses besteht darin, dass Sie in längeren Ruheintervallen programmieren können, der Thread fast sofort gestoppt wird (wenn Sie es sonstsleep()
tun würden) und meiner Meinung nach der Code für die Behandlung des Exits erheblich einfacher ist .quelle
Es ist besser, wenn Sie keinen Thread töten. Eine Möglichkeit könnte darin bestehen, einen "try" -Block in den Zyklus des Threads einzufügen und eine Ausnahme auszulösen, wenn Sie den Thread stoppen möchten (z. B. break / return / ..., das Ihren for / while / ... stoppt). Ich habe dies auf meiner App verwendet und es funktioniert ...
quelle
Es ist definitiv möglich, eine
Thread.stop
Methode zu implementieren , wie im folgenden Beispielcode gezeigt:Die
Thread3
Klasse scheint Code ungefähr 33% schneller als dieThread2
Klasse auszuführen .quelle
self.__stop
Setzen in den Thread einzufügen. Beachten Sie, dass wie bei den meisten anderen Lösungen hier ein blockierender Aufruf nicht unterbrochen wird, da die Ablaufverfolgungsfunktion nur aufgerufen wird, wenn ein neuer lokaler Bereich eingegeben wird. Erwähnenswert ist auch, dass diessys.settrace
wirklich für die Implementierung von Debuggern, Profilen usw. gedacht ist und als solches als Implementierungsdetail von CPython betrachtet wird und in anderen Python-Implementierungen nicht garantiert vorhanden ist.Thread2
Klasse ist, dass sie Code ungefähr zehnmal langsamer ausführt . Einige Leute finden dies möglicherweise immer noch akzeptabel.t ist dein
Thread
Objekt.Lesen Sie die Python-Quelle (
Modules/threadmodule.c
undPython/thread_pthread.h
), die Sie sehen können , dass es sich umThread.ident
einenpthread_t
Typ handelt, damit Sie allespthread
tun können, was Sie bei der Verwendung von Python tun könnenlibpthread
.quelle
Die folgende Problemumgehung kann verwendet werden, um einen Thread zu beenden:
Dies kann sogar zum Beenden von Threads, deren Code in einem anderen Modul geschrieben ist, vom Hauptthread verwendet werden. Wir können eine globale Variable in diesem Modul deklarieren und damit Threads beenden, die in diesem Modul erzeugt wurden.
Normalerweise benutze ich dies, um alle Threads am Programm-Exit zu beenden. Dies ist möglicherweise nicht der perfekte Weg, um Threads zu beenden, könnte aber helfen.
quelle
Ich bin viel zu spät zu diesem Spiel gekommen, aber ich habe mit einer ähnlichen Frage gerungen, und das Folgende scheint das Problem perfekt für mich zu lösen UND ermöglicht mir einige grundlegende Überprüfungen und Bereinigungen des Thread-Status, wenn der dämonisierte Sub-Thread beendet wird:
Ausbeuten:
quelle
SystemExit
desafter_timeout
Threads etwas mit dem Haupt-Thread tun (der in diesem Beispiel einfach darauf wartet, dass der erstere beendet wird)?SystemExit
hat nur zwei besondere Eigenschaften: es erzeugt keine einen Traceback (wenn irgendwelche Thread beendet durch einen Wurf), und wenn die Haupt - Thread beendet , indem man wirft es setzt den Beendigungsstatus (während dennoch für andere nicht-Daemon - Threads wartet beenden).Eine Sache, die ich hinzufügen möchte, ist, dass wenn Sie die offizielle Dokumentation in Threading lib Python lesen , empfohlen wird, die Verwendung von "dämonischen" Threads zu vermeiden, wenn Sie nicht möchten, dass Threads abrupt mit dem von Paolo Rovelli erwähnten Flag enden .
Aus der offiziellen Dokumentation:
Ich denke, dass das Erstellen von dämonischen Threads von Ihrer Anwendung abhängt, aber im Allgemeinen (und meiner Meinung nach) ist es besser zu vermeiden, sie zu töten oder dämonisch zu machen. In der Mehrfachverarbeitung können Sie den
is_alive()
Prozessstatus überprüfen und "beenden", um sie zu beenden (außerdem vermeiden Sie GIL-Probleme). Manchmal treten jedoch weitere Probleme auf, wenn Sie Ihren Code in Windows ausführen.Und denken Sie immer daran, dass der Python-Interpreter ausgeführt wird, wenn Sie "Live-Threads" haben, um darauf zu warten. (Aufgrund dieser Dämonik kann Ihnen helfen, wenn keine Rolle abrupt endet).
quelle
Zu diesem Zweck wurde eine Bibliothek gebaut, stopit . Obwohl einige der hier aufgeführten Vorsichtsmaßnahmen immer noch gelten, bietet zumindest diese Bibliothek eine regelmäßige, wiederholbare Technik zum Erreichen des angegebenen Ziels.
quelle
Während es ziemlich alt ist, dies könnte eine praktische Lösung für einige sein:
Auf diese Weise kann ein "Thread Ausnahmen im Kontext eines anderen Threads auslösen". Auf diese Weise kann der terminierte Thread die Terminierung verarbeiten, ohne regelmäßig ein Abbruch-Flag zu überprüfen.
Laut der ursprünglichen Quelle gibt es jedoch einige Probleme mit diesem Code.
quelle
Dies scheint mit pywin32 unter Windows 7 zu funktionieren
quelle
Pieter Hintjens - einer der Gründer des ØMQ- Projekts - sagt, dass die Verwendung von ØMQ und die Vermeidung von Synchronisationsprimitiven wie Sperren, Mutexen, Ereignissen usw. der sicherste und sicherste Weg ist, Multithread-Programme zu schreiben:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
Dazu gehört, einem untergeordneten Thread mitzuteilen, dass er seine Arbeit abbrechen soll. Dies würde geschehen, indem der Thread mit einem ØMQ-Socket ausgestattet und an diesem Socket nach einer Nachricht abgefragt wird, die besagt, dass er abgebrochen werden soll.
Der Link enthält auch ein Beispiel für Multithread-Python-Code mit ØMQ.
quelle
Unter der Annahme, dass Sie mehrere Threads derselben Funktion haben möchten, ist dies meiner Meinung nach die einfachste Implementierung, um einen nach ID zu stoppen:
Das Schöne ist hier, Sie können mehrere gleiche und unterschiedliche Funktionen haben und sie alle stoppen
functionname.stop
Wenn Sie nur einen Thread der Funktion haben möchten, müssen Sie sich die ID nicht merken. Hör einfach auf, wenn
doit.stop
> 0.quelle
Nur um auf der Idee von @ SCB aufzubauen (was genau das war, was ich brauchte), um eine KillableThread-Unterklasse mit einer benutzerdefinierten Funktion zu erstellen:
Natürlich wartet der Thread wie bei @SBC nicht darauf, eine neue Schleife auszuführen, um anzuhalten. In diesem Beispiel wird die Meldung "Thread töten" direkt nach "Thread beenden" gedruckt, anstatt weitere 4 Sekunden auf den Abschluss des Threads zu warten (da wir bereits 6 Sekunden geschlafen haben).
Das zweite Argument im KillableThread-Konstruktor ist Ihre benutzerdefinierte Funktion (print_msg hier). Args-Argumente sind die Argumente, die beim Aufrufen der Funktion (("Hallo Welt")) hier verwendet werden.
quelle
Wie in der Antwort von @ Kozyarchuk erwähnt, funktioniert die Installation von Trace. Da diese Antwort keinen Code enthielt, finden Sie hier ein funktionsfähiges Beispiel:
Es stoppt nach dem Drucken
1
und2
.3
wird nicht gedruckt.quelle
Sie können Ihren Befehl in einem Prozess ausführen und ihn dann mithilfe der Prozess-ID beenden. Ich musste zwischen zwei Threads synchronisieren, von denen einer nicht von selbst zurückkehrt.
quelle
Starten Sie den Sub-Thread mit setDaemon (True).
quelle
So geht's:
Geben Sie ihm ein paar Sekunden Zeit, dann sollte Ihr Thread gestoppt werden. Überprüfen Sie auch die
thread._Thread__delete()
Methode.Der Einfachheit halber würde ich eine
thread.quit()
Methode empfehlen . Wenn Sie beispielsweise einen Socket in Ihrem Thread haben, würde ich empfehlen, einequit()
Methode in Ihrer Socket-Handle-Klasse zu erstellen , den Socket zu beenden und dann einthread._Thread__stop()
Inside in Ihrem auszuführenquit()
.quelle
_Thread__stop()
lediglich einen Thread als gestoppt , es stoppt den Thread nicht wirklich! Mach das niemals. Lesen Sie .Wenn Sie wirklich die Fähigkeit benötigen, eine Unteraufgabe zu beenden, verwenden Sie eine alternative Implementierung.
multiprocessing
undgevent
beide unterstützen das wahllose Töten eines "Fadens".Pythons Threading unterstützt keine Stornierung. Versuche es erst gar nicht. Es ist sehr wahrscheinlich, dass Ihr Code blockiert, Speicher beschädigt oder leckt oder andere unbeabsichtigte "interessante", schwer zu debuggende Effekte aufweist, die selten und nicht deterministisch auftreten.
quelle