Existe-t-il un moyen de tuer un fil?

767

Est-il possible de terminer un thread en cours d'exécution sans définir / vérifier aucun indicateur / sémaphores / etc.?

Sudden Def
la source

Réponses:

672

C'est généralement un mauvais schéma de tuer un thread brusquement, en Python et dans n'importe quelle langue. Pensez aux cas suivants:

  • le thread contient une ressource critique qui doit être fermée correctement
  • le thread a créé plusieurs autres threads qui doivent également être supprimés.

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

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

Dans ce code, vous devez appeler stop()le thread lorsque vous souhaitez qu'il se termine et attendre que le thread se ferme correctement à l'aide de join(). Le thread doit vérifier le drapeau d'arrêt à intervalles réguliers.

Il y a cependant des cas où vous avez vraiment besoin de tuer un thread. Un exemple est lorsque vous encapsulez une bibliothèque externe qui est occupée pour de longs appels et que vous souhaitez l'interrompre.

Le code suivant permet (avec certaines restrictions) de lever 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_SetAsyncExcsemble provenir d'une ancienne version de Python .)

Comme indiqué dans la documentation, ce n'est pas une solution miracle car si le thread est occupé en dehors de l'interpréteur Python, il ne détectera pas l'interruption.

Un bon modèle d'utilisation de ce code consiste à ce que le thread intercepte une exception spécifique et effectue le nettoyage. De cette façon, vous pouvez interrompre une tâche et toujours effectuer un nettoyage approprié.

Philippe F
la source
78
@ Bluebird75: De plus, je ne suis pas sûr d'avoir l'argument selon lequel les threads ne devraient pas être tués brusquement "car le thread peut contenir une ressource critique qui doit être fermée correctement": cela est également vrai à partir d'un programme principal et des programmes principaux peut être tué brutalement par l'utilisateur (Ctrl-C sous Unix, par exemple) - auquel cas il essaie de gérer cette possibilité aussi bien que possible. Donc, je ne vois pas ce qui est spécial avec les threads, et pourquoi ils ne devraient pas recevoir le même traitement que les programmes principaux (à savoir qu'ils peuvent être tués brusquement). :) Pouvez-vous développer?
Eric O Lebigot
18
@EOL: D'un autre côté, si toutes les ressources que le thread possède sont des ressources locales (fichiers ouverts, sockets), Linux est raisonnablement bon au nettoyage du processus et cela ne fuit pas. J'ai cependant eu des cas où j'ai créé un serveur à l'aide de socket, et si je fais une interruption brutale avec Ctrl-C, je ne peux plus lancer le programme car il ne peut pas lier le socket. Je dois attendre 5 minutes. La bonne solution était d'attraper Ctrl-C et de nettoyer la déconnexion des sockets.
Philippe F
10
@ Bluebird75: btw. vous pouvez utiliser l' SO_REUSEADDRoption socket pour éviter les Address already in useerreurs.
Messa
12
Remarque sur cette réponse: au moins pour moi (py2.6), je devais passer Noneau lieu de 0pour le res != 1cas, et je devais appeler ctypes.c_long(tid)et transmettre cela à n'importe quelle fonction ctypes plutôt qu'à la tid directement.
Walt W
21
Il convient de mentionner que _stop est déjà occupé dans la bibliothèque de threads Python 3. En tant que tel, utilisez peut-être une variable différente sinon vous obtiendrez une erreur.
deadthreetimes
113

Il n'y a pas d'API officielle pour le faire, non.

Vous devez utiliser l'API de la 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.

Notez que cela est intrinsèquement dangereux. Cela entraînera probablement des déchets non collectables (à partir des variables locales des cadres de pile qui deviennent des déchets) et peut entraîner des blocages, si le thread en cours de suppression a le GIL au moment où il est tué.

Martin c. Löwis
la source
26
Il va conduire à des blocages si le fil en question détient le GIL.
Matthias Urlichs du
96

Une multiprocessing.Processboîtep.terminate()

Dans les cas où je veux tuer un thread, mais que je ne veux pas utiliser de drapeaux / verrous / signaux / sémaphores / événements / quoi que ce soit, je promeut les threads en processus complets. Pour le code qui utilise seulement quelques threads, la surcharge n'est pas si mauvaise.

Par exemple, cela est pratique pour terminer facilement les "threads" d'aide qui exécutent le blocage des E / S

La conversion est triviale: dans le code associé, remplacez all threading.Threadwith multiprocessing.Processet all queue.Queuewith multiprocessing.Queueet ajoutez les appels requis de p.terminate()à votre processus parent qui veut tuer son enfantp

Voir la documentation Python pourmultiprocessing .

cfi
la source
Merci. J'ai remplacé queue.Queue par multiprocessing.JoinableQueue et j'ai suivi cette réponse: stackoverflow.com/a/11984760/911207
David Braun
Beaucoup de pages sur cette question. Cela semble la solution évidente pour beaucoup je pense
geotheory
6
multiprocessingest agréable, mais sachez que les arguments sont conservés dans le nouveau processus. Donc, si l'un des arguments n'est pas sélectionnable (comme a logging.log), ce n'est peut-être pas une bonne idée de l'utiliser multiprocessing.
Lyager le
1
multiprocessingles arguments sont conservés dans le nouveau processus sous Windows, mais Linux utilise des fourches pour les copier (Python 3.7, je ne sais pas quelles autres versions). Vous vous retrouverez donc avec du code qui fonctionne sous Linux mais qui soulève des erreurs de cornichon sous Windows.
nyanpasu64
multiprocessingavec l'exploitation forestière est une affaire délicate. Besoin d'utiliser QueueHandler(voir ce tutoriel ). Je l'ai appris à la dure.
Fanchen Bao
74

Si vous essayez de terminer l'ensemble du programme, vous pouvez définir le thread comme un "démon". voir Thread.daemon

schettino72
la source
Cela n'a aucun sens. La documentation indique clairement, "cela doit être défini avant que start () soit appelé, sinon RuntimeError est déclenché." Ainsi, si je veux tuer un thread qui n'était pas à l'origine un démon, comment puis-je l'utiliser?
Raffi Khatchadourian
27
Raffi Je pense qu'il suggère que vous le définissiez à l'avance, sachant que lorsque votre thread principal se termine, vous souhaitez également que les threads démon se terminent.
fantabolous
1
Je ne sais pas pourquoi ce n'est pas la réponse acceptée
eric
La définition d'un thread en tant que démon n'est-elle pas quelque chose que vous feriez si vous souhaitez que le thread continue de fonctionner même si votre programme principal s'arrête?
Michele Piccolini
Vous, monsieur, êtes mon héros du jour. Exactement ce que je cherchais et rien à ajouter.
Blizz
42

Comme d'autres l'ont mentionné, la norme est de définir un drapeau d'arrêt. Pour quelque chose de léger (pas de sous-classement de Thread, pas de variable globale), un rappel lambda est une option. (Notez les parenthèses 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()

Remplacement print()par une pr()fonction qui rince toujours (sys.stdout.flush() ) peut améliorer la précision de la sortie du shell.

(Testé uniquement sur Windows / Eclipse / Python3.3)

Jon Coombs
la source
1
Vérifié sous Linux / Python 2.7, fonctionne comme un charme. Cela devrait être la réponse officielle, c'est beaucoup plus simple.
Paul Kenjora
1
Vérifié sur Linux Ubuntu Server 17.10 / Python 3.6.3 et fonctionne.
Marcos
1
Également vérifié en 2.7. Une si belle réponse!
silgon
Qu'est-ce que la pr()fonction?
Alper
35

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

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

Cela n'a été testé que sur Python 2.7.3, mais il est susceptible de fonctionner avec d'autres versions 2.x récentes.

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
la source
J'utilise quelque chose comme ça pour donner un fil à mes fils KeyboardInterruptafin qu'ils aient une chance de nettoyer. S'ils sont toujours suspendus après cela, alors SystemExitc'est approprié, ou tout simplement tuer le processus à partir d'un terminal.
drevicko
Cela fonctionne si le thread est en cours d'exécution. Cela ne fonctionne pas si le thread est dans un appel système; l'exception sera silencieusement ignorée.
Matthias Urlichs
@MatthiasUrlichs une idée de comment détecter l'état d'exécution du thread, pour pouvoir imprimer un avertissement ou réessayer?
Johan Dahlin
1
@JohanDahlin Vous pouvez attendre un peu (ce qui, si vous voulez réessayer, vous devez le faire quand même), puis faire le test isAlive (). Dans tous les cas, même si cela fonctionnerait, je ne garantirais pas non plus qu'il ne laisse pas de références pendantes. Bien qu'il soit possible en théorie de sécuriser la destruction des threads dans CPython, une utilisation judicieuse de pthread_cleanup_push()/_pop()ce serait beaucoup de travail à implémenter correctement et ralentirait sensiblement l'interpréteur.
Matthias Urlichs
32

Vous ne devez jamais tuer de force un thread sans coopérer avec lui.

Tuer un thread supprime toutes les garanties que les blocs try / finally sont configurés afin que vous puissiez laisser les verrous verrouillés, les fichiers ouverts, etc.

La seule fois où vous pouvez affirmer que tuer des threads de force est une bonne idée est de tuer un programme rapidement, mais jamais un seul thread.

Lasse V. Karlsen
la source
11
Pourquoi est-il si difficile de simplement dire un fil, tuez-vous s'il vous plaît lorsque vous avez terminé votre boucle actuelle ... Je ne comprends pas.
Mehdi
2
Il n'y a pas de mécanisme intégré dans le processeur pour identifier une "boucle" en tant que telle, le mieux que vous puissiez espérer est d'utiliser une sorte de signal que le code qui est actuellement à l'intérieur de la boucle vérifiera une fois qu'il se sera terminé. La bonne façon de gérer la synchronisation des threads est par des moyens coopératifs, la suspension, la reprise et la suppression des threads sont des fonctions destinées aux débogueurs et au système d'exploitation, pas le code d'application.
Lasse V. Karlsen
2
@Mehdi: si j'écris (personnellement) le code dans le fil, oui, je suis d'accord avec vous. Mais il y a des cas où j'exécute des bibliothèques tierces et je n'ai pas accès à la boucle d'exécution de ce code. Il s'agit d'un cas d'utilisation pour la fonctionnalité demandée.
Dan H
@DanH C'est encore pire avec du code tiers car vous n'avez aucune idée des dommages qu'il peut causer. Si votre bibliothèque tierce n'est pas suffisamment robuste pour nécessiter d'être supprimée, vous devez effectuer l'une des actions suivantes: (1) demander à l'auteur de résoudre le problème, (2) utiliser autre chose. Si vous n'avez vraiment pas le choix, alors mettre ce code dans un processus distinct devrait être plus sûr car certaines ressources ne sont partagées que dans un seul processus.
Phil1970
25

En Python, vous ne pouvez tout simplement pas tuer un thread directement.

Si vous n'avez PAS vraiment besoin d'un thread (!), Ce que vous pouvez faire, au lieu d'utiliser le package de thread , est d'utiliser le package de multitraitement . Ici, pour tuer un processus, vous pouvez simplement appeler la méthode:

yourProcess.terminate()  # kill the process!

Python va tuer votre processus (sous Unix via le signal SIGTERM, tandis que sous Windows via le TerminateProcess() appel). Faites attention à l'utiliser lorsque vous utilisez une file d'attente ou un tuyau! (cela peut corrompre les données de la file d'attente / du canal)

Notez que le multiprocessing.Eventet lemultiprocessing.Semaphore travail exactement de la même manière que le threading.Eventet lethreading.Semaphore respectivement. En fait, les premiers sont des clones de ces derniers.

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

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

Le programme principal se fermera lorsqu'il ne restera plus de threads non démon vivants. En d'autres termes, lorsque votre thread principal (qui est, bien sûr, un thread non démon) terminera ses opérations, le programme se fermera même s'il reste des threads démon qui fonctionnent.

Notez qu'il est nécessaire de définir un fil comme daemon avant d' start()appeler la méthode!

Bien sûr, vous pouvez et devez utiliser daemon même avec multiprocessing. Ici, lorsque le processus principal se termine, il tente de mettre fin à tous ses processus enfants démoniaques.

Enfin, s'il vous plaît, notez que sys.exit()et os.kill()ne sont pas des choix.

Paolo Rovelli
la source
14

Vous pouvez tuer un thread en installant trace dans le thread qui quittera le thread. Voir le lien ci-joint pour une implémentation possible.

Tuez un fil en Python

Kozyarchuk
la source
Je l'ai déjà vu. Cette solution est basée sur la vérification du drapeau self.killed
Sudden Def
1
L'une des rares réponses ici qui FONCTIONNE réellement
Ponkadoodle
5
Deux problèmes avec cette solution: (a) l'installation d'un traceur avec sys.settrace () rendra votre thread plus lent. Jusqu'à 10 fois plus lent s'il est lié au calcul. (b) n'affectera pas votre thread lorsqu'il est dans un appel système.
Matthias Urlichs
10

Si vous appelez explicitement time.sleep()dans le cadre de votre thread (par exemple, interroger un service externe), une amélioration de la méthode de Phillipe consiste à utiliser le délai d'attente dans le event'swait() méthode où que voussleep()

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

Ensuite, pour 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 d' sleep()ing et de vérifier régulièrement l'événement est que vous pouvez programmer dans des intervalles de sommeil plus longs, le thread est arrêté presque immédiatement (alors que vous seriez autrement sleep()ing) et à mon avis, le code de gestion de la sortie est beaucoup plus simple .

SCB
la source
3
pourquoi ce post a-t-il été rejeté? Quel est le problème avec ce message? Cela ressemble exactement à ce dont j'ai besoin ....
JDOaktown
9

C'est mieux si vous ne tuez pas un fil. Un moyen pourrait être d'introduire un bloc "try" dans le cycle du thread et de lever une exception lorsque vous voulez arrêter le thread (par exemple un break / return / ... qui arrête votre pour / while / ...). Je l'ai utilisé sur mon application et cela fonctionne ...

Giancarlo
la source
8

Il est certainement possible d'implémenter une Thread.stopméthode comme indiqué dans 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 Thread3classe semble exécuter le code environ 33% plus rapidement que la Thread2classe.

Noctis Skytower
la source
3
C'est une manière intelligente d'injecter des chèques pour self.__stopêtre mis dans le fil. Notez que comme la plupart des autres solutions ici, elle n'interrompra pas réellement un appel de blocage, car la fonction de trace n'est appelée que lorsqu'une nouvelle portée locale est entrée. Il convient également de noter que cela est sys.settracevraiment destiné à l'implémentation de débogueurs, de profils, etc.
dano
3
@dano: L'un des plus gros problèmes de la Thread2classe est qu'elle exécute le code environ dix fois plus lentement. Certaines personnes pourraient toujours trouver cela acceptable.
Noctis Skytower
+1 sur ce point ralentit considérablement l'exécution du code. Je suggère à l'auteur de cette solution d'inclure ces informations dans la réponse.
Vishal
6
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

t est votreThread objet.

Lisez la source de python ( Modules/threadmodule.cet Python/thread_pthread.h) vous pouvez voir Thread.identest un pthread_ttype, donc vous pouvez faire tout ce que vous pthreadpouvez faire en utilisation python libpthread.

snyh
la source
Et comment faites-vous cela sous Windows?
iChux
12
Vous ne le faites pas; pas sur Windows et pas sur Linux non plus. Raison: le thread en question peut contenir le GIL pendant que vous faites cela (Python libère le GIL lorsque vous appelez en C). Si c'est le cas, votre programme se bloquera instantanément. Même si ce n'est pas le cas, enfin: les blocs ne seront pas exécutés, etc., c'est donc une idée très dangereuse.
Matthias Urlichs du
6

La solution de contournement suivante peut être utilisée 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 les threads générés dans ce module.

J'utilise habituellement cela pour terminer tous les threads à la sortie du programme. Ce n'est peut-être pas le moyen idéal pour terminer un ou plusieurs threads, mais cela peut aider.

Amit Chahar
la source
Pouces vers le haut. Simple à comprendre.
alyssaeliyah du
6

Je suis bien en retard dans ce jeu, mais j'ai été confronté à une question similaire et les éléments suivants semblent à la fois résoudre le problème parfaitement pour moi ET me permettent de vérifier et de nettoyer l'état du thread de base lorsque le sous-thread démonifié se termine:

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

Rendements:

0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
slumtrimpet
la source
Ceci est une excellente réponse qui devrait être plus élevée sur la liste
drootang
Pourquoi augmenter SystemExitsur le after_timeoutthread ferait quoi que ce soit au thread principal (qui attend simplement que le premier quitte dans cet exemple)?
Davis Herring
@DavisHerring Je ne sais pas trop à quoi tu veux en venir. SystemExit tue le thread principal, pourquoi pensez-vous qu'il NE FERA PAS quoi que ce soit sur le thread principal? Sans cet appel, le programme continuera simplement à attendre sur le thread enfant. Vous pouvez également ctrl + c ou utiliser tout autre moyen pour tuer le thread principal, mais ceci est un exemple.
slumtrimpet
@slumtrimpet: SystemExitn'a que deux propriétés spéciales: il ne produit pas de traceback (lorsqu'un thread se ferme en en lançant un), et si le thread principal se ferme en en lançant un, il définit l'état de sortie (tout en attendant d'autres threads non démon pour quitter).
Davis Herring
@DavisHerring Oh, je me suis trompé. Totalement d'accord avec toi. Je me souvenais mal de ce que nous avons fait ici et j'ai mal compris votre commentaire.
slumtrimpet
4

Une chose que je veux ajouter est que si vous lisez la documentation officielle dans le threading lib Python , il est recommandé d'éviter l'utilisation de threads "démoniaques", lorsque vous ne voulez pas que les threads se terminent brusquement, avec le drapeau que Paolo Rovelli a mentionné .

De la documentation officielle:

Les threads démons sont brusquement arrêtés à l'arrêt. Leurs ressources (telles que les fichiers ouverts, les transactions de base de données, etc.) peuvent ne pas être libérées correctement. Si vous souhaitez que vos threads s'arrêtent correctement, rendez-les non démoniaques et utilisez un mécanisme de signalisation approprié tel qu'un événement.

Je pense que la création de threads démoniaques dépend de votre application, mais en général (et à mon avis), il vaut mieux éviter de les tuer ou de les rendre démoniaques. En multitraitement, vous pouvez utiliser is_alive()pour vérifier l'état du processus et «terminer» pour les terminer (vous évitez également les problèmes GIL). 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 des "threads en direct", l'interpréteur Python s'exécutera pour les attendre. (En raison de ce démon peut vous aider si la matière ne se termine pas brusquement).

Chema
la source
4
Je ne comprends pas le dernier paragraphe.
tshepang
@Tshepang Cela signifie que s'il y a des threads non démoniaques en cours d'exécution dans votre application, l'interpréteur Python continuera de fonctionner jusqu'à ce que tous les threads non démons soient terminés . Si vous ne vous souciez pas si les threads se terminent à la fin du programme, leur création peut être utile.
Tom Myddeltyn
3

Il y a une bibliothèque construite à cet effet, stopit . Bien que certaines des mêmes 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 déclaré.

Jason R. Coombs
la source
1

Bien qu'il soit plutôt ancien, cela pourrait être une solution pratique pour certains:

Un petit module qui étend les fonctionnalités du module de threading - permet à un thread de lever des exceptions dans le contexte d'un autre thread. En augmentant SystemExit, vous pouvez enfin tuer les threads 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 lever des exceptions dans le contexte d'un autre thread» et de cette manière, le thread terminé peut gérer la terminaison sans vérifier régulièrement un indicateur d'abandon.

Cependant, selon sa source d'origine , ce code présente certains problèmes.

  • L'exception ne sera levée que lors de l'exécution de bytecode python. Si votre thread appelle une fonction de blocage native / intégrée, l'exception ne sera levée que lorsque l'exécution reviendra au code python.
    • Il existe également un problème si la fonction intégrée appelle en interne PyErr_Clear (), ce qui annulerait efficacement votre exception en attente. Vous pouvez essayer de le relever à nouveau.
  • Seuls les types d'exceptions peuvent être levés en toute sécurité. Les instances d'exception sont susceptibles de provoquer un comportement inattendu et sont donc limitées.
  • J'ai demandé à exposer cette fonction dans le module de thread intégré, mais comme ctypes est devenu une bibliothèque standard (à partir de 2.5), et que cette
    fonctionnalité n'est probablement pas indépendante de l'implémentation, elle peut rester
    non exposée.
wp78de
la source
0

Cela semble fonctionner avec pywin32 sur Windows 7

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

Pieter Hintjens - l'un des fondateurs du projet ØMQ - dit qu'utiliser ØMQ et éviter les primitives de synchronisation comme les verrous, les mutex, les événements, etc. est la façon la plus sûre et la plus sûre d'écrire des programmes multi-threads:

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

Cela inclut de dire à un thread enfant qu'il doit annuler son travail. Cela serait fait en équipant le thread d'un ØMQ-socket et en interrogeant ce socket pour un message disant qu'il devrait annuler.

Le lien fournit également un exemple de code python multi-thread avec ØMQ.

paulkernstock
la source
0

En supposant que vous souhaitez avoir plusieurs threads de la même fonction, c'est à mon humble avis la mise en œuvre la plus simple pour en arrêter un par id:

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

La bonne chose est ici, vous pouvez avoir plusieurs fonctions identiques et différentes, et les arrêter toutes en functionname.stop

Si vous voulez avoir un seul thread de la fonction, vous n'avez pas besoin de vous souvenir de l'id. Arrêtez-vous si doit.stop> 0.

rundekugel
la source
0

Juste pour développer l'idée de @ SCB (qui était exactement ce dont j'avais besoin) pour créer une sous-classe KillableThread avec une fonction personnalisée:

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

Naturellement, comme avec @SBC, le thread n'attend pas pour exécuter une nouvelle boucle pour s'arrêter. Dans cet exemple, vous verriez le message "Killing Thread" imprimé juste après le "About to kill thread" au lieu d'attendre 4 secondes de plus pour que le thread se termine (puisque nous avons déjà dormi pendant 6 secondes).

Le deuxième argument du constructeur KillableThread est votre fonction personnalisée (print_msg ici). L'argument Args sont les arguments qui seront utilisés lors de l'appel de la fonction (("hello world")) ici.

Tim Meehan
la source
0

Comme mentionné dans la réponse de @ Kozyarchuk , l'installation de traces fonctionne. Puisque cette réponse ne contenait aucun code, voici un exemple fonctionnel prêt à l'emploi:

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

Il s'arrête après avoir imprimé 1et 2. 3n'est pas imprimé.

Basj
la source
-1

Vous pouvez exécuter votre commande dans un processus, puis la tuer à l'aide de l'ID de processus. J'avais besoin de synchroniser entre deux threads dont l'un ne revient pas 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;
user1942887
la source
-1

Démarrez 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
Sud
la source
-2

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

Voici comment procéder:

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 devrait être arrêté. Vérifiez égalementthread._Thread__delete() méthode.

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

DoXiD
la source
J'ai dû utiliser self._Thread__stop () à l'intérieur de mon objet threading.Thread pour l'arrêter. Je ne comprends pas pourquoi un simple self.join () comme cet exemple ( code.activestate.com/recipes/65448-thread-control-idiom ) ne fonctionne pas.
harijay
12
Plus de détails sur "cela n'arrête pas vraiment un fil" seraient utiles.
2371
19
Fondamentalement, l'appel de la méthode _Thread__stop n'a aucun effet en dehors de dire à Python que le thread est arrêté. Il peut en fait continuer à fonctionner. Voir gist.github.com/2787191 pour un exemple.
Bluehorn
35
C'est tout à fait faux. marque_Thread__stop() simplement un thread comme arrêté , il n'arrête pas réellement le thread! Ne fais jamais ça. Lisez .
dotancohen
-2

Si vous avez vraiment besoin de pouvoir tuer une sous-tâche, utilisez une autre implémentation. multiprocessinget geventtous deux soutiennent sans discernement la suppression d'un «fil».

Le threading de Python ne prend pas en charge l'annulation. N'essaye même pas. Votre code est très susceptible de bloquer, de corrompre ou de fuir la mémoire, ou d'avoir d'autres effets difficiles à déboguer "intéressants" qui se produisent rarement et de manière non déterministe.

Matthias Urlichs
la source
2
… Et oui, je sais que les deux ne sont pas strictement «threading», mais ils fonctionnent tous les deux si votre code correspond (ou peut être fait pour s'adapter) à leur modèle.
Matthias Urlichs