Y a-t-il un moyen de tuer un fil en Python?

est-il possible de terminer un thread en cours d'exécution sans mettre/vérifier les drapeaux/sémaphores/etc.?

597
demandé sur igaurav 2008-11-27 17:55:53

24 réponses

c'est généralement un mauvais modèle de tuer un fil brusquement, en Python et dans n'importe quelle langue. Pensez aux cas suivants:

  • le fil conducteur est la tenue d'une ressource critique qui doit être fermé correctement
  • le fil a créé plusieurs autres threads qui doivent être tués.

la bonne façon de gérer cela si vous pouvez vous le permettre (si vous gérez vos propres fils) est d'avoir un exit_request indique que chaque threads vérifie l'intervalle régulier pour voir s'il est temps pour lui de sortir.

par exemple:

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

dans ce code, vous devez appeler stop() sur le thread quand vous voulez qu'il sorte, et attendre que le thread sorte correctement en utilisant join(). Le thread doit vérifier le drapeau d'arrêt à intervalles réguliers.

Il ya des cas cependant où vous avez vraiment besoin de tuer un fil. Un par exemple, lorsque vous enveloppez une bibliothèque externe qui est occupée pour de longs appels et que vous voulez l'interrompre.

le code suivant permet (avec quelques restrictions) de soulever une Exception dans un thread Python:

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 )

(basé sur Killable Threads de Tomer Filiba. La citation sur la valeur de retour de PyThreadState_SetAsyncExc semble être d'une ancienne version de Python .)

As noté dans la documentation, Ce n'est pas une balle magique car si le thread est occupé en dehors de L'interpréteur Python, il n'attrapera pas l'interruption.

un bon usage de ce code est d'avoir le thread attraper une exception spécifique et effectuer le nettoyage. De cette façon, vous pouvez interrompre une tâche et avoir encore le nettoyage approprié.

546
répondu Philippe F 2018-08-14 03:56:33

il n'y a pas D'API officielle pour faire ça, non.

vous devez utiliser l'API plate-forme pour tuer le thread, par exemple Pthread_kill, ou TerminateThread. Vous pouvez accéder à cette API par exemple via pythonwin, ou via ctypes.

remarquez que c'est intrinsèquement dangereux. Il en résultera probablement des déchets non récupérables (à partir des variables locales des cadres de cheminée qui deviennent des déchets), et peut conduire à des blocages, si le fil tué a la GIL au moment où il est tué.

99
répondu Martin v. Löwis 2008-11-27 15:08:07

Un multiprocessing.Process peut p.terminate()

dans les cas où je veux tuer un thread, mais ne veux pas utiliser les drapeaux/serrures/signaux/sémaphores/événements/quoi que ce soit, je fais la promotion des threads à des processus complets. Pour le code qui utilise juste quelques threads, le overhead n'est pas si mauvais.

E. G. cela est pratique pour terminer facilement les "threads" de helper qui exécutent le blocage I / O

la conversion est triviale: en code apparenté remplacer tous les threading.Thread par multiprocessing.Process et tous les queue.Queue par multiprocessing.Queue et ajouter les appels requis de p.terminate() à votre processus parent qui veut tuer son enfant p

Python doc

64
répondu cfi 2011-10-13 09:38:20

si vous essayez de terminer le programme entier, vous pouvez définir le thread comme un"démon". voir Thread.daemon

57
répondu schettino72 2009-11-18 15:54:53

vous ne devez jamais tuer de force un fil sans coopérer avec elle.

tuer un thread supprime toutes les garanties que try/finally bloque mis en place afin que vous puissiez laisser les serrures verrouillées, les fichiers ouverts, etc.

la seule fois où vous pouvez argumenter que tuer des threads par la force est une bonne idée est de tuer un programme rapidement, mais jamais de threads simples.

29
répondu Lasse Vågsæther Karlsen 2008-11-27 14:58:49

Ceci est basé sur thread2 -- tuables threads (Python recette)

vous devez appeler PyThreadState_SetasyncExc(), qui n'est disponible que via les ctypes.

cela n'a été testé que sur Python 2.7.3, mais il est probable que cela fonctionne avec d'autres 2 récents.x libère.

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")
28
répondu Johan Dahlin 2016-07-28 08:56:28

Comme d'autres l'ont mentionné, la norme est de fixer un stop drapeau. Pour quelque chose de léger (pas de sous-classement de Thread, pas de variable globale), un lambda callback est une option. (Noter les parenthèses dans 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()

le remplacement de print() par une fonction pr() qui tire toujours la chasse d'eau ( sys.stdout.flush() ) peut améliorer la précision de la sortie du réservoir.

(testé seulement sur Windows/Eclipse/Python3.3)

25
répondu Jon Coombs 2014-12-03 00:07:06

en Python, vous ne pouvez tout simplement pas tuer un fil directement.

si vous N'avez pas vraiment besoin d'avoir un Thread (!), ce que vous pouvez faire, au lieu d'utiliser le threading package , est d'utiliser la multiprocessing package . Ici, pour tuer un processus, vous pouvez simplement appeler la méthode:

yourProcess.terminate()  # kill the process!

Python va tuer votre processus (sur Unix par le signal SIGTERM, alors que sur Windows par l'appel TerminateProcess() ). Faites attention de l'utiliser en utilisant une Queue ou un tuyau! (il peut corrompre les données dans la File d'attente/Pipe)

noter que le multiprocessing.Event et le multiprocessing.Semaphore fonctionnent exactement de la même manière que le threading.Event et le threading.Semaphore respectivement. En fait, les premiers sont des clones de ces derniers.

si vous avez vraiment besoin d'utiliser un Thread, il n'y a aucun moyen de le tuer directement. Ce que vous pouvez faire, cependant, est d'utiliser un "fil de démon" . En fait, en Python, un fil peut être marqué comme démon :

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

le programme principal se terminera lorsqu'il ne restera plus de threads vivants non-démon. En d'autres termes, quand votre thread principal (qui est, bien sûr, un thread non-daemon) terminera ses opérations, le programme se terminera même s'il y a encore quelques threads de daemon qui fonctionnent.

Note qu'il est nécessaire de définir un Thread comme daemon avant que la méthode start() s'appelle!

bien sûr, vous pouvez et devez utiliser daemon même avec multiprocessing . Ici, quand le processus principal sort, il tente de mettre fin à tous ses processus enfantins démoniaques.

enfin, veuillez noter que sys.exit() et os.kill() ne sont pas des choix.

18
répondu Paolo Rovelli 2017-04-24 02:16:31

vous pouvez tuer un thread en installant trace dans le thread qui va sortir le thread. Voir le lien ci-joint pour une mise en œuvre possible.

tuer un fil en Python

13
répondu Kozyarchuk 2015-01-14 01:40:37

il vaut mieux ne pas tuer un fil. Une façon pourrait être d'introduire un bloc" essayer " dans le cycle du thread et de lancer une exception quand vous voulez arrêter le thread (par exemple un break/return/... qui arrête votre pendant / tandis/...). J'ai utilisé ça sur mon application et ça marche...

9
répondu Giancarlo 2008-11-27 15:12:07

il est certainement possible de mettre en œuvre une méthode Thread.stop comme le montre l'exemple de code suivant:

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

la classe Thread3 semble exécuter le code environ 33% plus rapidement que la classe Thread2 .

7
répondu Noctis Skytower 2015-10-13 15:37:29
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

t est votre Thread objet.

Lire la source de python ( Modules/threadmodule.c et Python/thread_pthread.h ) vous pouvez voir le Thread.ident est un type pthread_t , donc vous pouvez faire tout ce que pthread peut faire en python utiliser libpthread .

7
répondu snyh 2017-04-24 02:16:42

une chose que je veux ajouter est que si vous lisez la documentation officielle dans filetage lib Python , il est recommandé d'éviter l'utilisation de fils" démoniaques", quand vous ne voulez pas de fils se terminent abruptement, avec le drapeau que Paolo Rovelli mentionné .

de la documentation officielle:

Les fils de démon

sont brusquement arrêtés à l'arrêt. Leurs ressources (telles que les fichiers ouverts, base de données transactions, etc.) peut ne pas être libérée correctement. Si vous voulez que vos threads s'arrêtent avec élégance, faites-les non-daemonic et utilisez un mécanisme de signalisation approprié tel qu'un événement.

je pense que la création de fils daemonic dépend de votre application, mais en général (et à mon avis) il est préférable d'éviter de les tuer ou de les rendre daemonic. Dans multiprocessing vous pouvez utiliser is_alive() pour vérifier l'état du processus et "Terminer" pour les terminer (aussi vous éviter Gil problems). Mais vous pouvez trouver plus de problèmes, parfois, lorsque vous exécutez votre code dans Windows.

et rappelez-vous toujours que si vous avez" live threads", L'interpréteur Python Les exécutera. (À cause de ce démoniaque peut vous aider si ne pas la matière se termine abruptement).

4
répondu Chema 2017-05-23 12:34:41

je suis en retard à ce jeu, mais j'ai été aux prises avec une question similaire et ce qui suit semble à la fois résoudre le problème parfaitement pour moi et me permet de faire quelques vérification de l'état du fil de base et le nettoyage lorsque le sous-fil démonisé sort:

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

Donne:

0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
2
répondu slumtrimpet 2018-05-22 18:37:59

C'est une mauvaise réponse, voir les commentaires

Voici comment le faire:

from threading import *

...

for thread in enumerate():
    if thread.isAlive():
        try:
            thread._Thread__stop()
        except:
            print(str(thread.getName()) + ' could not be terminated'))

Donnez-lui quelques secondes, puis votre fil doit être arrêté. Vérifier également la méthode thread._Thread__delete() .

je recommande une méthode thread.quit() pour plus de commodité. Par exemple, si vous avez une socket dans votre thread, je vous recommande de créer une méthode quit() dans votre socket-handle. classe, terminer la prise, puis exécuter un thread._Thread__stop() à l'intérieur de votre quit() .

1
répondu DoXiD 2015-07-30 02:11:07

démarrer le sous-thread avec 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
répondu Sud 2016-05-17 20:13:43

alors qu'il est plutôt vieux, ce pourrait être une solution pratique pour certains:

un petit module qui étend la fonctionnalité du module de filetage -- permet à un thread de lever des exceptions dans le contexte d'un autre fil. En soulevant SystemExit , vous pouvez enfin tuer les fils python.

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)

ainsi, il permet à un "thread de soulever des exceptions dans le contexte d'un autre thread" et de cette façon, le thread terminé peut gérer la terminaison sans vérifier régulièrement un indicateur d'abandon.

cependant, selon sa source originale , Il ya quelques problèmes avec ce code.

  • l'exception ne sera soulevée que lors de l'exécution du bytecode python. Si votre thread appelle une fonction de blocage native/intégrée, la exception sera levée que lorsque l'exécution retourne à l'python code.
    • il y a aussi un problème si la fonction intégrée appelle en interne PyErr_Clear(), ce qui annulerait effectivement votre exception en attente. Vous pouvez essayer de le soulever à nouveau.
  • seuls les types d'exception peuvent être levés en toute sécurité. Les cas d'Exception sont susceptibles de causer un comportement inattendu, et sont donc limités.
  • j'ai demandé d'exposer cette fonction dans le module de thread intégré, mais puisque ctypes est devenu une bibliothèque standard (à partir de 2.5), et ce

    la fonctionnalité n'est pas susceptible d'être mise en œuvre-agnostique, il peut être conservé

    non exposés.
0
répondu wp78de 2017-11-24 07:15:56

Pieter Hintjens -- l'un des fondateurs du projet ØMQ -- dit, en utilisant ØMQ et en évitant les primitives de synchronisation comme les serrures, les Mutex, les événements, etc., est la façon la plus saine et la plus sûre d'écrire des programmes multi-threadés:

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

Cela inclut dire à un fil d'enfant, qu'il devrait annuler son travail. Cela se ferait en équipant le fil avec un ømq-socket et le sondage sur cette socket pour un message disant qu'il devrait annuler.

le lien fournit également un exemple de code Python multi-threadé avec ØMQ.

0
répondu paulkernstock 2018-02-09 11:18:10

contournement suivant peut être utilisé pour tuer un thread:

kill_threads = False

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

thread.start_new_thread(doSomething, ())

cela peut être utilisé même pour terminer les threads, dont le code est écrit dans un autre module, à partir du thread principal. Nous pouvons déclarer une variable globale dans ce module et l'utiliser pour terminer le/les thread (s) généré (s) dans ce module.

Je l'utilise habituellement pour terminer tous les threads à la sortie du programme. Ce n'est peut-être pas le moyen idéal pour terminer thread/s mais pourrait aider.

0
répondu Amit Chahar 2018-03-14 21:59:07

si vous appelez explicitement time.sleep() comme partie de votre thread( par exemple, un sondage sur un service externe), une amélioration sur la méthode de Phillipe est d'utiliser le délai dans le event 's wait() méthode partout où vous sleep()

par exemple:

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

puis l'exécuter

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

l'avantage d'utiliser wait() au lieu de sleep() et de vérifier régulièrement l'événement est que vous pouvez programmer dans des intervalles plus longs de sommeil, le fil est arrêté presque immédiatement (alors que vous seriez autrement sleep() ing) et à mon avis, le code pour la manipulation de sortie est beaucoup plus simple.

0
répondu SCB 2018-04-17 12:05:19

il y a une bibliothèque construite à cet effet, stopit . Bien que certaines des mises en garde énumérées ici s'appliquent toujours, au moins cette bibliothèque présente une technique régulière et reproductible pour atteindre l'objectif énoncé.

0
répondu Jason R. Coombs 2018-07-23 23:53:12

cela semble fonctionner avec pywin32 sur windows 7

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
-1
répondu zzart 2015-07-29 10:59:06

vous pouvez exécuter votre commande dans un processus et ensuite la tuer en utilisant l'identifiant du processus. J'avais besoin de synchroniser entre deux thread dont l'un ne revient pas tout seul.

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;
-1
répondu user1942887 2016-04-14 19:01:09

Si vous avez vraiment besoin de la capacité de tuer une sous-tâche, utilisez une autre application. multiprocessing et gevent soutiennent tous deux le meurtre sans distinction d'un "fil".

le threading de Python ne supporte pas l'annulation. N'essayez même pas. Votre code est très susceptible de bloquer, de corrompre ou de faire fuir la mémoire, ou d'avoir d'autres effets "intéressants" involontaires difficiles à déboguer qui se produisent rarement et de façon non décisive.

-2
répondu Matthias Urlichs 2016-01-17 06:21:26