Ist es möglich, einen laufenden Thread zu beenden, ohne Flags / Semaphoren / etc. Zu setzen / zu überprüfen?
Ist es möglich, einen laufenden Thread zu beenden, ohne Flags / Semaphoren / etc. Zu setzen / zu überprüfen?
Antworten:
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, um Address already in use
Fehler zu vermeiden .
None
statt 0
für den res != 1
Fall übergeben, und ich musste das aufrufen ctypes.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.
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
durch multiprocessing.Process
und alle queue.Queue
durch multiprocessing.Queue
und fügen Sie die erforderlichen Aufrufe p.terminate()
Ihres übergeordneten Prozesses hinzu, der sein untergeordnetes Element töten möchtep
Siehe die Python-Dokumentation fürmultiprocessing
.
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 a logging.log
), ist es möglicherweise keine gute Idee, es zu verwenden multiprocessing
.
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 werden QueueHandler
(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
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()
.)
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 __name__ == '__main__':
main()
Das Ersetzen print()
durch eine pr()
Funktion, die immer spült ( sys.stdout.flush()
), kann die Genauigkeit der Shell-Ausgabe verbessern.
(Nur unter Windows / Eclipse / Python3.3 getestet)
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.
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")
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.
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:
yourProcess.terminate() # kill the process!
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 das multiprocessing.Semaphore
genau so funktionieren wie das threading.Event
und das threading.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 :
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 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 dem start()
Aufruf der Methode festgelegt werden muss!
Natürlich können und sollten Sie daemon
auch mit verwenden multiprocessing
. Wenn der Hauptprozess beendet wird, versucht er hier, alle seine dämonischen untergeordneten Prozesse zu beenden.
Schließlich Bitte beachten Sie, dass sys.exit()
und os.kill()
nicht Entscheidungen.
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.
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 von event
's zu wait()
verwenden, wo immer Sie sich befindensleep()
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 auszuführen
t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread
Der Vorteil der Verwendung wait()
anstelle der sleep()
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 sonst sleep()
tun würden) und meiner Meinung nach der Code für die Behandlung des Exits erheblich einfacher ist .
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 ...
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 __name__ == '__main__':
main()
Die Thread3
Klasse scheint Code ungefähr 33% schneller als die Thread2
Klasse auszuführen .
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 dies sys.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.
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t ist dein Thread
Objekt.
Lesen Sie die Python-Quelle ( Modules/threadmodule.c
und Python/thread_pthread.h
), die Sie sehen können , dass es sich um Thread.ident
einen pthread_t
Typ handelt, damit Sie alles pthread
tun können, was Sie bei der Verwendung von Python tun können libpthread
.
Die folgende Problemumgehung kann verwendet werden, um einen Thread zu beenden:
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, 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.
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:
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()
Ausbeuten:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
SystemExit
des after_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:
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äß gestoppt werden, machen Sie sie nicht dämonisch und verwenden Sie einen geeigneten Signalisierungsmechanismus, z. B. ein Ereignis.
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).
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.
Während es ziemlich alt ist, dies könnte eine praktische Lösung für einige sein:
Ein kleines Modul, das die Modulfunktionalität des Threadings erweitert - ermöglicht es einem Thread, Ausnahmen im Kontext eines anderen Threads auszulösen. Durch Erhöhen
SystemExit
können Sie endlich Python-Threads 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". 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.
- Die Ausnahme wird nur beim Ausführen von Python-Bytecode ausgelöst. 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 Ihre 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 es als einen gemeldet. Weitere Informationen finden Sie unter http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- Ich habe darum gebeten, diese Funktion im integrierten Thread-Modul verfügbar zu machen. Da ctypes jedoch zu einer Standardbibliothek geworden ist (ab 2.5) und diese
Funktion wahrscheinlich nicht implementierungsunabhängig ist, wird sie möglicherweise nicht
verfügbar gemacht.
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.
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:
import time
from threading import Thread
def doit(id=0):
doit.stop=0
print("start id:%d"%id)
while 1:
time.sleep(1)
print(".")
if doit.stop==id:
doit.stop=0
break
print("end thread %d"%id)
t5=Thread(target=doit, args=(5,))
t6=Thread(target=doit, args=(6,))
t5.start() ; t6.start()
time.sleep(2)
doit.stop =5 #kill t5
time.sleep(2)
doit.stop =6 #kill t6
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.
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:
from threading import Thread, Event
class KillableThread(Thread):
def __init__(self, sleep_interval=1, target=None, name=None, args=(), kwargs={}):
super().__init__(None, target, name, args, kwargs)
self._kill = Event()
self._interval = sleep_interval
print(self._target)
def run(self):
while True:
# Call custom function with arguments
self._target(*self._args)
# 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()
if __name__ == '__main__':
def print_msg(msg):
print(msg)
t = KillableThread(10, print_msg, args=("hello world"))
t.start()
time.sleep(6)
print("About to kill thread")
t.kill()
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.
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:
import sys, threading, time
class TraceThread(threading.Thread):
def __init__(self, *args, **keywords):
threading.Thread.__init__(self, *args, **keywords)
self.killed = False
def start(self):
self._run = self.run
self.run = self.settrace_and_run
threading.Thread.start(self)
def settrace_and_run(self):
sys.settrace(self.globaltrace)
self._run()
def globaltrace(self, frame, event, arg):
return self.localtrace if event == 'call' else None
def localtrace(self, frame, event, arg):
if self.killed and event == 'line':
raise SystemExit()
return self.localtrace
def f():
while True:
print('1')
time.sleep(2)
print('2')
time.sleep(2)
print('3')
time.sleep(2)
t = TraceThread(target=f)
t.start()
time.sleep(2.5)
t.killed = True
Es stoppt nach dem Drucken 1
und 2
. 3
wird nicht gedruckt.
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.
processIds = []
def executeRecord(command):
print(command)
process = subprocess.Popen(command, stdout=subprocess.PIPE)
processIds.append(process.pid)
print(processIds[0])
#Command that doesn't return by itself
process.stdout.read().decode("utf-8")
return;
def recordThread(command, timeOut):
thread = Thread(target=executeRecord, args=(command,))
thread.start()
thread.join(timeOut)
os.kill(processIds.pop(), signal.SIGINT)
return;
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 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, eine quit()
Methode in Ihrer Socket-Handle-Klasse zu erstellen , den Socket zu beenden und dann ein thread._Thread__stop()
Inside in Ihrem auszuführen quit()
.
_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
und gevent
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.