Gibt es eine Möglichkeit, einen Thread zu töten?

766

Ist es möglich, einen laufenden Thread zu beenden, ohne Flags / Semaphoren / etc. Zu setzen / zu überprüfen?

Plötzliche Def
quelle

Antworten:

672

Es ist im Allgemeinen ein schlechtes Muster, einen Thread in Python und in jeder Sprache abrupt zu beenden. Denken Sie an folgende Fälle:

  • Der Thread enthält eine kritische Ressource, die ordnungsgemäß geschlossen werden muss
  • Der Thread hat mehrere andere Threads erstellt, die ebenfalls beendet werden müssen.

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_SetAsyncExcscheint 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.

Philippe F.
quelle
78
@ Bluebird75: Außerdem bin ich mir nicht sicher, ob ich das Argument bekomme, dass Threads nicht abrupt beendet werden sollten, "weil der Thread möglicherweise eine kritische Ressource enthält, die ordnungsgemäß geschlossen werden muss": Dies gilt auch für ein Hauptprogramm und Hauptprogramme kann vom Benutzer abrupt beendet werden (z. B. Strg-C unter Unix) - in diesem Fall versuchen sie, mit dieser Möglichkeit so gut wie möglich umzugehen. Ich verstehe also nicht, was das Besondere an Threads ist und warum sie nicht die gleiche Behandlung wie Hauptprogramme erhalten sollten (nämlich, dass sie abrupt getötet werden können). :) Könnten Sie das näher erläutern?
Eric O Lebigot
18
@EOL: Wenn andererseits alle Ressourcen, die der Thread besitzt, lokale Ressourcen sind (offene Dateien, Sockets), ist Linux bei der Prozessbereinigung einigermaßen gut und dies leckt nicht. Ich hatte jedoch Fälle, in denen ich einen Server mit Socket erstellt habe und wenn ich eine brutale Unterbrechung mit Strg-C mache, kann ich das Programm nicht mehr starten, da es den Socket nicht binden kann. Ich muss 5 Minuten warten. Die richtige Lösung bestand darin, Strg-C zu fangen und die Verbindung zum Socket sauber zu trennen.
Philippe F
10
@ Bluebird75: Übrigens. Sie können die SO_REUSEADDRSocket-Option verwenden, um Address already in useFehler zu vermeiden .
Messa
12
Anmerkung zu dieser Antwort: Zumindest für mich (py2.6) musste ich Nonestatt 0für den res != 1Fall übergeben, und ich musste das aufrufen ctypes.c_long(tid)und an eine beliebige ctypes-Funktion übergeben, anstatt direkt an die tid.
Walt W
21
Erwähnenswert ist, dass _stop bereits in der Python 3-Threading-Bibliothek belegt ist. Verwenden Sie daher möglicherweise eine andere Variable, da sonst eine Fehlermeldung angezeigt wird.
dreimal am
113

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.

Martin v. Löwis
quelle
26
Es wird zu Deadlocks führen , wenn der Faden in Frage , die GIL hält.
Matthias Urlichs
96

Eine multiprocessing.ProcessDosep.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.Threaddurch multiprocessing.Processund alle queue.Queuedurch multiprocessing.Queueund 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 .

cfi
quelle
Vielen Dank. Ich ersetzte queue.Queue durch multiprocessing.JoinableQueue und folgte dieser Antwort: stackoverflow.com/a/11984760/911207
David Braun
Viele Seiten zu diesem Thema. Dies scheint die offensichtliche Lösung für viele zu sein, denke ich
Geotheory
6
multiprocessingist 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.
Lyager
1
multiprocessingArgumente 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.
Nyanpasu64
multiprocessingmit der Protokollierung ist eine knifflige Angelegenheit. Muss verwendet werden QueueHandler(siehe dieses Tutorial ). Ich habe es auf die harte Tour gelernt.
Fanchen Bao
74

Wenn Sie versuchen, das gesamte Programm zu beenden, können Sie den Thread als "Daemon" festlegen. siehe Thread.daemon

schettino72
quelle
Das macht keinen Sinn. In der Dokumentation heißt es eindeutig: "Dies muss festgelegt werden, bevor start () aufgerufen wird, andernfalls wird RuntimeError ausgelöst." Wie kann ich diesen Thread verwenden, wenn ich einen Thread beenden möchte, der ursprünglich kein Daemon war?
Raffi Khatchadourian
27
Raffi Ich denke, er schlägt vor, dass Sie es im Voraus festlegen, da Sie wissen, dass beim Beenden Ihres Hauptthreads auch die Daemon-Threads beendet werden sollen.
fantastisch
1
Ich
bin
Ist das Festlegen eines Threads als Daemon nicht etwas, das Sie tun würden, wenn der Thread weiterhin ausgeführt werden soll, selbst wenn Ihr Hauptprogramm heruntergefahren wird?
Michele Piccolini
Sie, mein Herr, sind mein Held des Tages. Genau das, wonach ich gesucht habe und überhaupt keine Aufregung.
Blizz
42

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)

Jon Coombs
quelle
1
Verifiziert unter Linux / Python 2.7, funktioniert wie ein Zauber. Dies sollte die offizielle Antwort sein, es ist viel einfacher.
Paul Kenjora
1
Verifiziert unter Linux Ubuntu Server 17.10 / Python 3.6.3 und funktioniert.
Marcos
1
Auch in 2.7 verifiziert. So eine schöne Antwort!
Silgon
Was ist pr()Funktion?
Alper
35

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")
Johan Dahlin
quelle
Ich benutze so etwas, um meinen Threads eine zu geben, KeyboardInterruptdamit sie eine Chance haben, aufzuräumen. Wenn sie danach NOCH hängen, SystemExitist dies angemessen, oder beenden Sie den Vorgang einfach von einem Terminal aus.
Drevicko
Dies funktioniert, wenn der Thread gerade ausgeführt wird. Es funktioniert nicht, wenn sich der Thread in einem Systemaufruf befindet. Die Ausnahme wird stillschweigend ignoriert.
Matthias Urlichs
@MatthiasUrlichs eine Idee, wie man den Thread-Ausführungsstatus erkennt, um eine Warnung drucken oder erneut versuchen zu können?
Johan Dahlin
1
@JohanDahlin Sie könnten etwas warten (was Sie, wenn Sie es erneut versuchen möchten, trotzdem tun müssen) und dann den isAlive () -Test durchführen. Während dies funktionieren würde, würde ich auf keinen Fall garantieren, dass es keine baumelnden Referenzen gibt. Während es theoretisch möglich ist, das Töten von Threads in CPython sicher zu machen pthread_cleanup_push()/_pop(), wäre die korrekte Implementierung eine Menge Arbeit und würde den Interpreter merklich verlangsamen.
Matthias Urlichs
32

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.

Lasse V. Karlsen
quelle
11
Warum ist es so schwer, nur einen Thread zu erzählen, bitte töte dich selbst, wenn du deine aktuelle Schleife beendet hast ... Ich verstehe es nicht.
Mehdi
2
In die CPU ist kein Mechanismus eingebaut, um eine "Schleife" als solche zu identifizieren. Das Beste, auf das Sie hoffen können, ist die Verwendung eines Signals, das der Code, der sich derzeit in der Schleife befindet, nach dem Beenden überprüft. Der richtige Weg, um die Thread-Synchronisation zu handhaben, ist kooperativ. Das Anhalten, Fortsetzen und Beenden von Threads ist eine Funktion, die für Debugger und das Betriebssystem bestimmt ist, nicht für Anwendungscode.
Lasse V. Karlsen
2
@ Mehdi: Wenn ich (persönlich) den Code in den Thread schreibe, ja, ich stimme dir zu. Es gibt jedoch Fälle, in denen ich Bibliotheken von Drittanbietern verwende und keinen Zugriff auf die Ausführungsschleife dieses Codes habe. Dies ist ein Anwendungsfall für die angeforderte Funktion.
Dan H
@DanH Mit Code von Drittanbietern ist es sogar noch schlimmer, da Sie keine Ahnung haben, welchen Schaden er verursachen kann. Wenn Ihre Drittanbieter-Bibliothek nicht robust genug ist, um getötet zu werden, sollten Sie einen der folgenden Schritte ausführen: (1) Bitten Sie den Autor, das Problem zu beheben, (2) verwenden Sie etwas anderes. Wenn Sie wirklich keine Wahl haben, sollte es sicherer sein, diesen Code in einen bestimmten Prozess zu integrieren, da einige Ressourcen nur innerhalb eines einzelnen Prozesses gemeinsam genutzt werden.
Phil1970
25

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.Eventund das multiprocessing.Semaphoregenau so funktionieren wie das threading.Eventund 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 daemonvor dem start()Aufruf der Methode festgelegt werden muss!

Natürlich können und sollten Sie daemonauch 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.

Paolo Rovelli
quelle
14

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

Kozyarchuk
quelle
Ich habe es bereits gesehen. Diese Lösung basiert auf Self.Killed Flag Check
Sudden Def
1
Eine der wenigen Antworten hier, die tatsächlich funktioniert
Ponkadoodle
5
Zwei Probleme mit dieser Lösung: (a) Wenn Sie einen Tracer mit sys.settrace () installieren, wird Ihr Thread langsamer ausgeführt. Bis zu 10-mal langsamer, wenn es rechengebunden ist. (b) wirkt sich nicht auf Ihren Thread aus, während er sich in einem Systemaufruf befindet.
Matthias Urlichs
10

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 .

SCB
quelle
3
Warum wurde dieser Beitrag abgelehnt? Was ist los mit diesem Beitrag? Es sieht genau so aus, wie ich es brauche ...
JDOaktown
9

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 ...

Giancarlo
quelle
8

Es ist definitiv möglich, eine Thread.stopMethode 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 Thread3Klasse scheint Code ungefähr 33% schneller als die Thread2Klasse auszuführen .

Noctis Skytower
quelle
3
Dies ist eine clevere Methode, um Überprüfungen für das self.__stopSetzen 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.settracewirklich 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.
Dano
3
@dano: Eines der größten Probleme mit der Thread2Klasse ist, dass sie Code ungefähr zehnmal langsamer ausführt . Einige Leute finden dies möglicherweise immer noch akzeptabel.
Noctis Skytower
+1 verlangsamt die Codeausführung erheblich. Ich würde vorschlagen, dass der Autor dieser Lösung diese Informationen in die Antwort einbezieht.
Vishal
6
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

t ist dein ThreadObjekt.

Lesen Sie die Python-Quelle ( Modules/threadmodule.cund Python/thread_pthread.h), die Sie sehen können , dass es sich um Thread.identeinen pthread_tTyp handelt, damit Sie alles pthreadtun können, was Sie bei der Verwendung von Python tun können libpthread.

snyh
quelle
Und wie macht man das unter Windows?
iChux
12
Sie nicht; nicht unter Windows und auch nicht unter Linux. Grund: Der betreffende Thread enthält möglicherweise die GIL, während Sie dies tun (Python gibt die GIL frei, wenn Sie C aufrufen). In diesem Fall blockiert Ihr Programm sofort. Auch wenn dies nicht der Fall ist: Blöcke werden nicht ausgeführt usw. Dies ist also eine sehr unsichere Idee.
Matthias Urlichs
6

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.

Amit Chahar
quelle
Daumen hoch. Einfach zu verstehen.
Alyssaeliyah
6

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]
Slumtrimpet
quelle
Dies ist eine ausgezeichnete Antwort, die weiter oben auf der Liste stehen sollte
Drootang
Warum sollte das Erhöhen SystemExitdes after_timeoutThreads etwas mit dem Haupt-Thread tun (der in diesem Beispiel einfach darauf wartet, dass der erstere beendet wird)?
Davis Herring
@DavisHerring Ich bin mir nicht sicher, worauf du hinaus willst. SystemExit beendet den Hauptthread. Warum sollte es Ihrer Meinung nach nichts am Hauptthread tun? Ohne diesen Aufruf wartet das Programm einfach weiter auf den untergeordneten Thread. Sie können auch Strg + C drücken oder andere Mittel verwenden, um den Hauptthread zu beenden. Dies ist jedoch ein Beispiel.
Slumtrimpet
@slumtrimpet: SystemExithat 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).
Davis Herring
@ DavidHerring Oh, ich hab's verstanden. Ich stimme dir vollkommen zu. Ich habe mich falsch daran erinnert, was wir hier gemacht haben, und Ihren Kommentar falsch verstanden.
Slumtrimpet
4

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

Chema
quelle
4
Ich verstehe den letzten Absatz nicht.
Tshepang
@Tshepang Dies bedeutet, dass der Python-Interpreter so lange ausgeführt wird, bis alle Nicht-Daemon- Threads fertig sind , wenn in Ihrer Anwendung nicht-dämonische Threads ausgeführt werden . Wenn es Ihnen egal ist, ob die Threads beim Beenden des Programms enden, kann es hilfreich sein, sie als Daemon zu verwenden.
Tom Myddeltyn
3

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.

Jason R. Coombs
quelle
1

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 SystemExitkö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.
  • 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.
wp78de
quelle
0

Dies scheint mit pywin32 unter Windows 7 zu funktionieren

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
zzart
quelle
0

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.

paulkernstock
quelle
0

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.

rundekugel
quelle
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.

Tim Meehan
quelle
0

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 1und 2. 3wird nicht gedruckt.

Basj
quelle
-1

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;
user1942887
quelle
-1

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
Sud
quelle
-2

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

DoXiD
quelle
Ich musste self._Thread__stop () in meinem Threading.Thread-Objekt verwenden, damit es stoppt. Ich verstehe nicht, warum ein einfaches self.join () wie dieses Beispiel ( code.activestate.com/recipes/65448-thread-control-idiom ) nicht funktioniert.
Harijay
12
Weitere Details zu "Dies stoppt einen Thread nicht wirklich" wären hilfreich.
2371
19
Grundsätzlich hat das Aufrufen der Methode _Thread__stop keine Auswirkung, außer Python mitzuteilen, dass der Thread gestoppt ist. Es kann tatsächlich weiterlaufen. Ein Beispiel finden Sie unter gist.github.com/2787191 .
Bluehorn
35
Das ist einfach falsch. markiert_Thread__stop() lediglich einen Thread als gestoppt , es stoppt den Thread nicht wirklich! Mach das niemals. Lesen Sie .
Dotancohen
-2

Wenn Sie wirklich die Fähigkeit benötigen, eine Unteraufgabe zu beenden, verwenden Sie eine alternative Implementierung. multiprocessingund geventbeide 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.

Matthias Urlichs
quelle
2
… Und ja, ich weiß, dass beide nicht ausschließlich "Threading" sind, aber beide funktionieren, wenn Ihr Code zu ihrem Modell passt (oder angepasst werden kann).
Matthias Urlichs