webentwicklung-frage-antwort-db.com.de

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

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

688
Sudden Def

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:

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

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.

620
Philippe F

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.

111

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

Python doc

80
cfi

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

63
schettino72

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")
36
Johan Dahlin

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)

31
Jon Coombs

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.

23
Paolo Rovelli

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.

Töte einen Thread in Python

13
Kozyarchuk

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

9
Giancarlo

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.

8
Noctis Skytower

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.

6
Amit Chahar
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.

6
snyh

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.

6
SCB

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

4
Chema

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]
4
slumtrimpet

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.

1
Jason R. Coombs

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

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
0
Sud

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

0
DoXiD

Dies scheint mit pywin32 unter Windows 7 zu funktionieren

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

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.

0
paulkernstock