Frage

Ist es möglich, einen laufenden Faden ohne Einstellung / Überprüfung jeder Flagge / Semaphore / etc zu beenden.?

War es hilfreich?

Lösung

Es ist im Allgemeinen ein schlechtes Muster einen Thread abrupt, in Python und in jeder Sprache zu töten. Denken Sie an den folgenden Fällen:

  • der Faden hält eine kritische Ressource, die richtig geschlossen werden müssen
  • hat der Faden mehrere andere Threads erstellt, die auch getötet werden müssen.

Die nette Art und Weise des Umgangs mit diesem, wenn Sie es sich leisten können (wenn Sie Ihre eigenen Threads verwalten) ist eine exit_request Flagge zu haben, die jeweils Faden Kontrollen regelmäßige Abstände, um zu sehen, ob es Zeit für sie ist zu beenden.

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() auf dem Thread aufrufen, wenn Sie es verlassen wollen, und warten Sie auf das Gewinde richtig mit join() verlassen. Der Faden sollte die Stoppmarke in regelmäßigen Abständen überprüfen.

Es gibt Fälle, aber wenn Sie wirklich einen Thread müssen töten. Ein Beispiel ist, wenn Sie eine externe Bibliothek wickeln, die für lange Anrufe besetzt ist und Sie wollen es zu unterbrechen.

Der folgende Code ermöglicht (mit einigen Einschränkungen) eine Ausnahme in einem Python-Thread zu erhöhen:

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 angreifbar Themen von Tomer Filiba. Das Zitat über den Rückgabewert von PyThreadState_SetAsyncExc erscheint um von einer alte Version von Python ).

Wie in der Dokumentation angegeben, dies ist kein Wundermittel, denn wenn der Faden außerhalb des Python-Interpreter beschäftigt ist, wird es nicht die Unterbrechung fängt.

Ein gutes Nutzungsmuster dieses Codes ist der Faden eine spezifische Ausnahme abfangen und die Bereinigung durchführt. Auf diese Weise können Sie eine Aufgabe unterbrechen und haben immer noch die richtige Bereinigung.

Andere Tipps

Es gibt keine offizielle API, das zu tun, nein.

Sie müssen Plattform-API verwenden, um den Thread zu töten, z.B. pthread_kill oder TerminateThread. Sie können eine solche API zugreifen z.B. durch PythonWin oder durch ctypes.

Beachten Sie, dass diese von Natur aus unsicher ist. Es wird wahrscheinlich uneinbringlich Müll (von lokalen Variablen des Stack-Frames, den Müll wird) führen und kann zu Deadlocks führen, wenn der Thread die GIL an der Stelle getötet hat, wenn er getötet wird.

A multiprocessing.Process kann p.terminate()

In den Fällen, in denen ich einen Thread töten will, aber nicht will, Flaggen / Schlösser / Signale / Semaphore / Ereignisse verwenden, / was auch immer, ich die Fäden ausgewachsene Prozesse fördern. Für Code, der Verwendung von nur wenigen Fäden der Aufwand macht, ist nicht so schlimm.

z. Das ist praktisch, Helfer „Fäden“ zu leicht beendet die Sperrung ausführen I / O

Die Umwandlung ist trivial: In der entsprechenden Code ersetzt all threading.Thread mit multiprocessing.Process und all queue.Queue mit multiprocessing.Queue und den erforderlichen Anrufen von p.terminate() zu Ihrem Eltern-Prozess hinzufügen, die ihr Kind p töten wollen

Python doc

Wenn Sie versuchen, das ganze Programm beenden Sie den Thread als „Dämon“ einstellen. sehen Thread.daemon

Dies basiert auf thread2 - angreifbar Threads (Python Rezept)

Sie müssen PyThreadState_SetasyncExc () aufrufen, die nur über ctypes ist.

Dies ist nur auf Python 2.7.3 getestet worden, aber es ist wahrscheinlich mit anderen aktuellen 2.x-Versionen arbeiten.

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 mit Gewalt einen Thread töten, ohne mit ihm zusammenarbeiten.

einen Thread Töten entfernt alle Garantien, die Blöcke try / finally so einrichten, können Sie Sperren gesperrt lassen, Dateien zu öffnen, etc.

Das einzige Mal, wenn Sie, dass mit Gewalt zu töten Fäden argumentieren kann, ist eine gute Idee ist, ein Programm schnell, aber nie einzelne Fäden zu töten.

Wie andere erwähnt haben, ist die Norm einen Stop-Flag zu setzen. Für etwas leicht (ohne Klassifizieren von Faden, keine globale Variable), ist ein Lambda-Callback-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()

Ersetzen print() mit einer pr() Funktion, die immer spült (sys.stdout.flush()) kann die Genauigkeit der Shell-Ausgabe verbessern.

(Es werden nur getestet auf Windows / Eclipse / Python3.3)

In Python, können Sie einfach einen Thread nicht direkt töten.

Wenn Sie nicht wirklich ein Thema haben müssen (!), Was Sie tun können, anstatt die Threading Paket zu verwenden, ist die Multiprozessing Paket . Hier wird ein Prozess zu töten, können Sie einfach die Methode aufrufen:

yourProcess.terminate()  # kill the process!

Python wird Ihren Prozess (unter Unix durch das SIGTERM Signal, während auf Windows durch den TerminateProcess() Anruf) töten. Achten Sie darauf, es zu verwenden, während eine Queue oder ein Rohr mit! (Es beschädigen kann die Daten in der Warteschlange / Pipe)

Beachten Sie, dass die multiprocessing.Event und die multiprocessing.Semaphore Arbeit genau in der gleichen Art und Weise des threading.Event und der threading.Semaphore sind. In der Tat sind die ersten, die Klone der beiden letztgenannten.

Wenn Sie wirklich ein Thema verwenden müssen, gibt es keine Möglichkeit, sie direkt zu töten. Was Sie tun können, ist jedoch ein "Dämon-Thread" zu verwenden, . In der Tat, in Python kann ein Thema wie gekennzeichnet wird Dämon :

yourThread.daemon = True  # set the Thread as a "daemon thread"

Das Hauptprogramm wird beendet, wenn keine alive nicht-Daemon-Threads gelassen werden. Mit anderen Worten, wenn die Haupt-Thread (was natürlich ist, ein Nicht-Daemon-Thread) seine Tätigkeit beenden wird, wird das Programm verlassen, auch wenn es arbeiten noch einige Daemon-Threads sind.

Beachten Sie, dass es notwendig ist, einen Thread als daemon zu setzen, bevor die start() Methode aufgerufen wird!

Natürlich können Sie und sollten daemon sogar mit multiprocessing verwenden. Hier wird, wenn der Hauptprozess beendet wird, versucht er, alle seine dämonischen Kind Prozesse zu beenden.

Schließlich Bitte beachten Sie, dass sys.exit() und os.kill() sind nicht Wahlen.

Es ist auf jeden Fall möglich, ein Thread.stop Verfahren zu implementieren, wie im folgenden Beispielcode dargestellt:

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 erscheint Code ca. 33% schneller als die Thread2 Klasse ausgeführt werden.

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 Python-Quelle (Modules/threadmodule.c und Python/thread_pthread.h) Sie können sehen, die Thread.ident ist ein pthread_t Typ, so dass Sie alles tun können pthread in Python libpthread tun können.

Nach Abhilfe verwendet werden kann, einen Thread zu töten:

kill_threads = False

def doSomething():
    global kill_threads
    while True:
        if kill_threads:
            thread.exit()
        ......
        ......

thread.start_new_thread(doSomething, ())

Dies kann auch für die Beendigung Fäden verwendet werden, dessen Code in einem anderen Modul geschrieben, von der Haupt-Thread. Wir können eine globale Variable in diesem Modul deklarieren und es verwenden Thread zu beenden / s in diesem Modul hervorgebracht.

ich dies in der Regel verwenden, um alle Fäden am Programmende zu kündigen. Dies ist vielleicht nicht die perfekte Art und Weise des Thread / s zu beenden, könnte aber helfen.

Eine Sache, die ich hinzufügen möchte, ist, dass, wenn Sie offizielle Dokumentation zu lesen in Threading lib Python , es wird empfohlen, die Verwendung von „dämonischen“ Fäden zu vermeiden, wenn Sie Threads nicht abrupt beendet werden soll, mit der Flagge, die Paolo Rovelli genannten .

Von offizieller Dokumentation:

  

Daemon Threads abrupt beim Herunterfahren gestoppt. Ihre Ressourcen (wie zB offene Dateien, Datenbank-Transaktionen, etc.) möglicherweise nicht ordnungsgemäß freigegeben werden. Wenn Sie Ihre Fäden anmutig stoppen wollen, machen sie nicht dämonisch und einen geeigneten Signalmechanismus, wie ein Ereignis verwendet werden.

Ich denke, dass dämonische Threads Erstellung Ihrer Anwendung ab, aber im Allgemeinen (und meiner Meinung nach) ist es besser, sie zu vermeiden oder zu töten sie dämonisch zu machen. In Multiprozessing Sie is_alive() verwenden können Prozessstatus und „beenden“ für Finish sie (auch Sie vermeiden GIL Probleme) zu überprüfen. Aber Sie können mehr Probleme finden, manchmal, wenn Sie Ihren Code in Windows ausführen.

Und immer daran denken, dass, wenn Sie „live Threads“ haben, wird der Python-Interpreter sie warten, ausgeführt werden. (Aufgrund dieser dämonischen kann Ihnen helfen, wenn Sie nicht Sache abrupt endet).

Wenn Sie explizit time.sleep() als Teil Ihres Themas nennend (sagt Polling einig externen Service), eine Verbesserung gegenüber Phillipes Methode ist das Timeout in der event der wait() Methode zu verwenden, wo immer Sie sleep()

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 ist es zu laufen

t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread

Der Vorteil der Verwendung wait() statt sleep()ing und regelmäßig das Ereignis überprüft ist, dass Sie in längeren Intervallen Schlaf programmieren können, wird der Faden fast sofort gestoppt (wenn Sie sonst werden sleep()ing) und meiner Meinung nach, der Code für die Handhabung Ausfahrt ist deutlich einfacher.

Ich bin viel zu spät, um dieses Spiel, aber ich habe mit einer ähnlichen Frage und folgendes erscheint Überprüfung und Bereinigung beide lösen für mich das Problem perfekt und läßt mich einigen grundlegenden Thread-Zustand tun, wenn die daemonisierte Unter Thread beendet:

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]

Es gibt eine Bibliothek für diesen Zweck gebaut, stopIt . Obwohl einige der gleichen Vorsichtsmaßnahmen hier aufgeführten noch gelten, zumindest diese Bibliothek stellt eine regelmäßige, wiederholbare Technik für das angegebene Ziel zu erreichen.

Dies scheint mit pywin32 auf windows 7

zu arbeiten
my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
  

Dies ist eine schlechte Antwort, siehe die Kommentare

Hier ist, wie es zu tun:

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 den Thread gestoppt werden soll. Prüfen Sie auch die thread._Thread__delete() Methode.

Ich habe gerne ein thread.quit() Verfahren zur Bequemlichkeit empfehlen. Zum Beispiel, wenn Sie eine Steckdose in Ihrem Thread haben, würde ich die Schaffung eine quit() Methode in der socket-Griff Klasse empfehlen, beenden Sie die Steckdose, dann ein thread._Thread__stop() Innenseite Ihres quit() ausgeführt werden.

Starten Sie den Unterfaden 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

Es ist zwar ziemlich alt, diese könnte ein sehr nützlich sein, Lösung für einige:

  

Ein kleines Modul, das das Einfädeln der Modulfunktionalität erweitert -   ermöglicht ein Gewinde Ausnahmen im Zusammenhang mit einer anderen zu erhöhen   Faden. Durch die Erhöhung SystemExit, können Sie schließlich Python Threads töten.

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)

So erlaubt es ein „Thread Ausnahmen im Zusammenhang mit einem anderen Thread zu erhöhen“ und auf diese Weise kann der Thread beendete die Beendigung handhaben, ohne dass ein Abbruch-Flag regelmäßig zu überprüfen.

Doch nach seiner Originalquelle , gibt es einige Probleme mit diesem Code.

  
      
  • Die Ausnahme wird nur angehoben werden, wenn Python-Bytecode ausführt. Wenn Ihr Thread ruft eine native / eingebaute Funktion blockiert, die   Ausnahme wird nur erhöht werden, wenn die Ausführung an die Python zurück   Code.      
        
    • Es ist auch ein Problem, wenn die integrierte Funktion ruft intern PyErr_Clear (), die ausstehende Ausnahme verworfen würde.   Sie können versuchen, es noch einmal zu erhöhen.
    •   
  •   
  • Nur Ausnahmetypen sicher angehoben werden. Ausnahme Fälle sind wahrscheinlich ein unerwartetes Verhalten verursachen, und sind somit eingeschränkt.      
  •   
  • Ich habe diese Funktion in dem, Einbau-Thread-Modul zu belichten, aber da ctypes hat eine Standardbibliothek werden (Stand 2.5), und diese
      Feature ist nicht wahrscheinlich, Implementierung unabhängig sein, es
    gehalten werden kann   nicht belichtet.
  •   

Pieter Hintjens - einer der Gründer des omq -project - sagt, mit omq und die Vermeidung von Synchronisations Primitiven, wie Schlösser, Mutexe, Events usw., ist die vernünftigste und sicherste Art und Weise Multi-threaded-Programme zu schreiben:

http://zguide.zeromq.org/py:all#Multithreading -mit-ZeroMQ

Dies schließt ein Kind Thread zu sagen, dass sie ihre Arbeit abbrechen sollte. Dies würde durch die Ausrüstung des Fadens mit einem omq-Buchse und Abfrage auf diesem Sockel für eine Nachricht durchgeführt werden sagt, dass es aufheben sollte.

Der Link liefert auch ein Beispiel für Multi-Threaded-Python-Code mit omq.

Sie können Ihren Befehl in einem Prozess ausführen und es dann töten den Prozess-ID verwenden. I benötigt, um zwischen zwei Fäden von denen ein von selbst nicht wieder synchronisieren.

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;

Wenn Sie wirklich die Fähigkeit benötigen eine Teilaufgabe zu töten, verwenden Sie eine alternative Implementierung. multiprocessing und beide gevent Unterstützung wahllos einen „Faden“.

Tötung

Pythons Threading unterstützt keine Stornierung. Versuche es erst gar nicht. Der Code ist sehr wahrscheinlich in einer Sackgasse, korrupt oder ein Speicherleck auf, oder haben andere unbeabsichtigte „interessant“ hard-to-Debug-Effekte, die selten und nichtdeterministisch passieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top