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 abrupt abzubrechen, und zwar in Python und in einer beliebigen Sprache. Denken Sie an folgende Fälle:
Wenn Sie es sich leisten können (wenn Sie Ihre eigenen Threads verwalten), können Sie dies auf einfache Weise tun, indem Sie ein exit_request-Flag verwenden, das jeder Thread in regelmäßigen Abständen überprüft, um festzustellen, ob es an der Zeit ist, dass er beendet wird.
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):
super(StoppableThread, self).__init__()
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()
für den Thread aufrufen, wenn dieser beendet werden soll, und mit join()
warten, bis der Thread ordnungsgemäß beendet wurde. 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 einbinden, die für lange Anrufe belegt ist und die Sie 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 Interpreters Python ausgelastet 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.
Dafür gibt es keine offizielle API, nein.
Sie müssen die Plattform-API verwenden, um den Thread abzubrechen, z. pthread_kill oder TerminateThread. Sie können auf eine solche API zugreifen, z. durch pythonwin oder durch ctypes.
Beachten Sie, dass dies von Natur aus unsicher ist. Es wird wahrscheinlich zu nicht sammelbarem Müll führen (von lokalen Variablen der Stapelrahmen, die zu Müll werden) und kann zu Deadlocks führen, wenn der Thread, der getötet wird, die GIL zu dem Zeitpunkt hat, an dem er getötet wird.
Ein multiprocessing.Process
kann p.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, stelle ich die Threads auf vollständige Prozesse um. Bei Code, der nur wenige Threads verwendet, ist der Overhead nicht so schlimm.
Z.B. Dies ist 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
durch multiprocessing.Process
und alle queue.Queue
durch multiprocessing.Queue
und fügen Sie die erforderlichen Aufrufe von p.terminate()
zu Ihrem übergeordneten Prozess hinzu, der will sein Kind töten p
Wenn Sie versuchen, das gesamte Programm zu beenden, können Sie den Thread als "Daemon" festlegen. siehe Thread.daemon
Dies basiert auf thread2 - killable threads (Python-Rezept)
Sie müssen PyThreadState_SetasyncExc () aufrufen, das nur über ctypes verfügbar ist.
Dies wurde nur auf Python 2.7.3 getestet, aber es wird wahrscheinlich mit anderen aktuellen 2.x-Versionen funktionieren.
import ctypes
def terminate_thread(thread):
"""Terminates a python thread from another thread.
:param thread: a threading.Thread instance
"""
if not thread.isAlive():
return
exc = ctypes.py_object(SystemExit)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(thread.ident), exc)
if res == 0:
raise ValueError("nonexistent 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(thread.ident, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
Sie sollten niemals einen Thread mit Gewalt abbrechen, ohne mit ihm zusammenzuarbeiten.
Durch das Beenden eines Threads werden alle Garantien beseitigt, die try/finally-Blöcke eingerichtet haben, sodass Sperren gesperrt, Dateien geöffnet usw. bleiben können.
Das einzige Mal, dass Sie argumentieren können, dass das gewaltsame Beenden von Threads eine gute Idee ist, ist das schnelle Beenden eines Programms, jedoch niemals einzelner Threads.
Wie bereits erwähnt, besteht die Norm darin, ein Stopp-Flag zu setzen. Für etwas Leichtes (keine Unterklassen von Thread, keine globale Variable) ist ein Lambda-Rückruf eine Option. (Beachten Sie die Klammern in if stop()
.)
import threading
import time
def do_work(id, stop):
print("I am thread", id)
while True:
print("I am thread {} doing something".format(id))
if stop():
print(" Exiting loop.")
break
print("Thread {}, signing off".format(id))
def main():
stop_threads = False
workers = []
for id in range(0,3):
tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads))
workers.append(tmp)
tmp.start()
time.sleep(3)
print('main: done sleeping; time to stop the threads.')
stop_threads = True
for worker in workers:
worker.join()
print('Finis.')
if __== '__main__':
main()
Das Ersetzen von print()
durch eine pr()
-Funktion, die immer einen Flush ausführt (sys.stdout.flush()
), kann die Präzision der Shell-Ausgabe verbessern.
(Nur unter Windows/Eclipse/Python3.3 getestet)
In Python können Sie einen Thread einfach nicht direkt beenden.
Wenn Sie keinen Thread (!) Benötigen, können Sie anstelle des threading -Pakets den Multiprocessing package . Um einen Prozess abzubrechen, können Sie hier einfach die Methode aufrufen:
yourProcess.terminate() # kill the process!
Python bricht Ihren Prozess ab (unter Unix über das SIGTERM-Signal, unter Windows über den Aufruf TerminateProcess()
). Achten Sie darauf, es zu benutzen, während Sie eine Queue oder Pipe benutzen! (Es kann die Daten in der Warteschlange/Pipe beschädigen.)
Beachten Sie, dass der multiprocessing.Event
und der multiprocessing.Semaphore
genau so funktionieren wie der threading.Event
bzw. der threading.Semaphore
. Tatsächlich sind die ersten Klone von letzteren.
Wenn Sie einen Thread WIRKLICH benötigen, können Sie ihn nicht direkt beenden. Was Sie jedoch tun können, ist die Verwendung eines "Daemon-Threads". In Python kann ein Thread sogar als daemon gekennzeichnet werden:
yourThread.daemon = True # set the Thread as a "daemon thread"
Das Hauptprogramm wird beendet, wenn keine aktiven Nicht-Daemon-Threads mehr vorhanden sind. Mit anderen Worten, wenn Ihr Haupt-Thread (der natürlich kein Dämon-Thread ist) seine Operationen beendet, wird das Programm beendet, auch wenn noch einige Dämon-Threads aktiv sind.
Beachten Sie, dass es erforderlich ist, einen Thread als daemon
festzulegen, bevor die start()
-Methode aufgerufen wird!
Natürlich können und sollten Sie daemon
auch mit multiprocessing
verwenden. Wenn der Hauptprozess beendet wird, versucht er hier, alle seine untergeordneten Dämonprozesse zu beenden.
Schließlich beachten Sie bitte, dass sys.exit()
und os.kill()
keine Auswahlmöglichkeiten sind.
Sie können einen Thread beenden, indem Sie eine Ablaufverfolgung in den Thread installieren, der den Thread beendet. Siehe beigefügten Link für eine mögliche Implementierung.
Es ist besser, wenn Sie keinen Thread töten. Eine Möglichkeit könnte sein, einen "try" -Block in den Thread-Zyklus einzufügen und eine Ausnahme auszulösen, wenn Sie den Thread anhalten möchten (zum Beispiel einen break/return/..., der Ihr for/while/... anhält). Ich habe dies in meiner App verwendet und es funktioniert ...
Es ist definitiv möglich, eine Thread.stop
-Methode zu implementieren, wie im folgenden Beispielcode gezeigt:
import sys
import threading
import time
class StopThread(StopIteration):
pass
threading.SystemExit = SystemExit, StopThread
class Thread2(threading.Thread):
def stop(self):
self.__stop = True
def _bootstrap(self):
if threading._trace_hook is not None:
raise ValueError('Cannot run thread with tracing!')
self.__stop = False
sys.settrace(self.__trace)
super()._bootstrap()
def __trace(self, frame, event, arg):
if self.__stop:
raise StopThread()
return self.__trace
class Thread3(threading.Thread):
def _bootstrap(self, stop_thread=False):
def stop():
nonlocal stop_thread
stop_thread = True
self.stop = stop
def tracer(*_):
if stop_thread:
raise StopThread()
return tracer
sys.settrace(tracer)
super()._bootstrap()
###############################################################################
def main():
test1 = Thread2(target=printer)
test1.start()
time.sleep(1)
test1.stop()
test1.join()
test2 = Thread2(target=speed_test)
test2.start()
time.sleep(1)
test2.stop()
test2.join()
test3 = Thread3(target=speed_test)
test3.start()
time.sleep(1)
test3.stop()
test3.join()
def printer():
while True:
print(time.time() % 1)
time.sleep(0.1)
def speed_test(count=0):
try:
while True:
count += 1
except StopThread:
print('Count =', count)
if __== '__main__':
main()
Die Thread3
-Klasse scheint Code ungefähr 33% schneller auszuführen als die Thread2
-Klasse.
Die folgende Problemumgehung kann zum Beenden eines Threads verwendet werden:
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
Dies kann sogar zum Beenden von Threads verwendet werden, deren Code in einem anderen Modul vom Haupt-Thread geschrieben wurde. Wir können eine globale Variable in diesem Modul deklarieren und sie verwenden, um in diesem Modul erzeugte Threads zu beenden.
Normalerweise benutze ich dies, um alle Threads am Programmende zu beenden. Dies ist möglicherweise nicht die perfekte Methode zum Beenden von Threads, kann jedoch Abhilfe schaffen.
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t ist Ihr Thread
Objekt.
Lesen Sie die Quelle python (Modules/threadmodule.c
und Python/thread_pthread.h
), Sie können sehen, dass Thread.ident
ein pthread_t
-Typ ist, also können Sie alles pthread
kann in python libpthread
verwendet werden.
Wenn Sie explizit time.sleep()
als Teil Ihres Threads aufrufen (z. B. einen externen Dienst abfragen), besteht eine Verbesserung der Methode von Phillipe darin, das Timeout in der Methode wait()
von event
zu verwenden, wo immer Sie sleep()
verwenden.
Zum Beispiel:
import threading
class KillableThread(threading.Thread):
def __init__(self, sleep_interval=1):
super().__init__()
self._kill = threading.Event()
self._interval = sleep_interval
def run(self):
while True:
print("Do Something")
# If no kill signal is set, sleep for the interval,
# If kill signal comes in while sleeping, immediately
# wake up and handle
is_killed = self._kill.wait(self._interval)
if is_killed:
break
print("Killing Thread")
def kill(self):
self._kill.set()
Dann, um es laufen zu lassen
t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread
Der Vorteil der Verwendung von wait()
anstelle von sleep()
und der 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 ansonsten sleep()
ing wären) und der Code für die Behandlung des Ausgangs meiner Meinung nach erheblich ist einfacher.
Eine Sache, die ich hinzufügen möchte, ist, dass, wenn Sie die offizielle Dokumentation in threading lib Python lesen, es empfohlen wird, die Verwendung von "dämonischen" Threads zu vermeiden, wenn Sie nicht möchten, dass Threads abrupt enden, mit dem Flag that Paolo Rovelli erwähnt .
Aus amtlichen Unterlagen:
Daemon-Threads werden beim Herunterfahren abrupt gestoppt. Ihre Ressourcen (wie geöffnete Dateien, Datenbanktransaktionen usw.) werden möglicherweise nicht ordnungsgemäß freigegeben. Wenn Sie möchten, dass Ihre Threads ordnungsgemäß angehalten werden, machen Sie sie nicht dämonisch und verwenden Sie einen geeigneten Signalisierungsmechanismus, z. B. ein Ereignis.
Ich denke, dass das Erstellen dämonischer Threads von Ihrer Anwendung abhängt, aber im Allgemeinen (und meiner Meinung nach) ist es besser, sie nicht zu töten oder dämonisch zu machen. Im Multiprocessing können Sie mit is_alive()
den Prozessstatus prüfen und zum Beenden "abbrechen" (Sie vermeiden auch GIL-Probleme). Manchmal treten jedoch weitere Probleme auf, wenn Sie Ihren Code unter Windows ausführen.
Und denken Sie immer daran, dass der Python -Interpreter ausgeführt wird, wenn Sie "Live-Threads" haben, um auf sie zu warten. (Wegen dieser Dämonik kann dir geholfen werden, wenn egal abrupt endet).
Ich bin viel zu spät in diesem Spiel, aber ich habe mit einer ähnlichen Frage gerungen und das Folgende scheint beide das Problem perfekt für mich UND zu lösen Ermöglicht mir die grundlegende Überprüfung und Bereinigung des Thread-Status, wenn der daemonisierte Sub-Thread beendet wird:
import threading
import time
import atexit
def do_work():
i = 0
@atexit.register
def goodbye():
print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
(i, threading.currentThread().ident))
while True:
print i
i += 1
time.sleep(1)
t = threading.Thread(target=do_work)
t.daemon = True
t.start()
def after_timeout():
print "KILL MAIN THREAD: %s" % threading.currentThread().ident
raise SystemExit
threading.Timer(2, after_timeout).start()
Erträge:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
Zu diesem Zweck gibt es eine Bibliothek, stopit . Obwohl einige der hier aufgeführten Vorsichtsmaßnahmen weiterhin gelten, bietet zumindest diese Bibliothek eine regelmäßige, wiederholbare Technik zum Erreichen des angegebenen Ziels.
Obwohl es ziemlich alt ist, könnte this für einige eine praktische Lösung sein:
Ein kleines Modul, das die Modulfunktionalität des Threadings erweitert - ermöglicht es einem Thread, Ausnahmen im Kontext eines anderen Threads auszulösen. Indem Sie
SystemExit
auslösen, können Sie python Threads endgültig beenden.
import threading
import ctypes
def _async_raise(tid, excobj):
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
if res == 0:
raise ValueError("nonexistent 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(tid, 0)
raise SystemError("PyThreadState_SetAsyncExc failed")
class Thread(threading.Thread):
def raise_exc(self, excobj):
assert self.isAlive(), "thread must be started"
for tid, tobj in threading._active.items():
if tobj is self:
_async_raise(tid, excobj)
return
# the thread was alive when we entered the loop, but was not found
# in the dict, hence it must have been already terminated. should we raise
# an exception here? silently ignore?
def terminate(self):
# must raise the SystemExit type, instead of a SystemExit() instance
# due to a bug in PyThreadState_SetAsyncExc
self.raise_exc(SystemExit)
Auf diese Weise kann ein "Thread Ausnahmen im Kontext eines anderen Threads auslösen", ohne regelmäßig ein Abbruch-Flag zu überprüfen.
Allerdings gibt es laut Originalquelle einige Probleme mit diesem Code.
- Die Ausnahme wird nur ausgelöst, wenn python Bytecode ausgeführt wird. Wenn Ihr Thread eine native/integrierte Blockierungsfunktion aufruft, wird die Ausnahme nur ausgelöst, wenn die Ausführung zum python-Code zurückkehrt.
- Es gibt auch ein Problem, wenn die integrierte Funktion PyErr_Clear () intern aufruft, wodurch die ausstehende Ausnahme effektiv abgebrochen wird. Sie können versuchen, es erneut zu erhöhen.
- Nur Ausnahmetypen können sicher ausgelöst werden. Ausnahmeinstanzen verursachen wahrscheinlich unerwartetes Verhalten und sind daher eingeschränkt.
- Zum Beispiel: t1.raise_exc (TypeError) und nicht t1.raise_exc (TypeError ("blah")).
- IMHO ist es ein Fehler, und ich habe ihn als einen gemeldet. Weitere Informationen erhalten Sie unter http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- Ich habe darum gebeten, diese Funktion im eingebauten Thread-Modul verfügbar zu machen, aber da ctypes zu einer Standardbibliothek (ab 2.5) geworden ist, und dies
Das Feature ist wahrscheinlich nicht implementierungsunabhängig, es kann jedoch beibehalten werden
unbelichtet.
Starten Sie den Sub-Thread mit setDaemon (True).
def bootstrap(_filename):
mb = ModelBootstrap(filename=_filename) # Has many Daemon threads. All get stopped automatically when main thread is stopped.
t = threading.Thread(target=bootstrap,args=('models.conf',))
t.setDaemon(False)
while True:
t.start()
time.sleep(10) # I am just allowing the sub-thread to run for 10 sec. You can listen on an event to stop execution.
print('Thread stopped')
break
Dies ist eine schlechte Antwort, siehe die Kommentare
So geht's:
from threading import *
...
for thread in enumerate():
if thread.isAlive():
try:
thread._Thread__stop()
except:
print(str(thread.getName()) + ' could not be terminated'))
Geben Sie ihm ein paar Sekunden, dann sollte Ihr Thread gestoppt werden. Überprüfen Sie auch die Methode thread._Thread__delete()
.
Ich würde der Einfachheit halber eine thread.quit()
-Methode empfehlen. Wenn Sie zum Beispiel einen Socket in Ihrem Thread haben, würde ich empfehlen, eine quit()
-Methode in Ihrer Socket-Handle-Klasse zu erstellen, den Socket zu beenden und dann eine thread._Thread__stop()
in Ihrem quit()
auszuführen. .
Dies scheint mit pywin32 unter Windows 7 zu funktionieren
my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
Laut Pieter Hintjens - einem der Gründer des ØMQ - Projekts - ist die Verwendung von ØMQ und das Vermeiden von Synchronisationsprimitiven wie Sperren, Mutexen, Ereignissen usw. die sicherste und sicherste Methode zum Schreiben von Multithreading Programme:
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 eine Meldung abgefragt wird, die besagt, dass er abgebrochen werden soll.
Der Link enthält auch ein Beispiel für Multithread-Code python mit ØMQ.