Est-il possible de terminer un thread en cours d'exécution sans définir / vérifier aucun indicateur / sémaphores / etc.?
la source
Est-il possible de terminer un thread en cours d'exécution sans définir / vérifier aucun indicateur / sémaphores / etc.?
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:
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_SetAsyncExc
semble 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é.
SO_REUSEADDR
option socket pour éviter lesAddress already in use
erreurs.None
au lieu de0
pour leres != 1
cas, et je devais appelerctypes.c_long(tid)
et transmettre cela à n'importe quelle fonction ctypes plutôt qu'à la tid directement.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é.
la source
Une
multiprocessing.Process
boî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.Thread
withmultiprocessing.Process
et allqueue.Queue
withmultiprocessing.Queue
et ajoutez les appels requis dep.terminate()
à votre processus parent qui veut tuer son enfantp
Voir la documentation Python pour
multiprocessing
.la source
multiprocessing
est 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 alogging.log
), ce n'est peut-être pas une bonne idée de l'utilisermultiprocessing
.multiprocessing
les 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.multiprocessing
avec l'exploitation forestière est une affaire délicate. Besoin d'utiliserQueueHandler
(voir ce tutoriel ). Je l'ai appris à la dure.Si vous essayez de terminer l'ensemble du programme, vous pouvez définir le thread comme un "démon". voir Thread.daemon
la source
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()
.)Remplacement
print()
par unepr()
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)
la source
pr()
fonction?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.
la source
KeyboardInterrupt
afin qu'ils aient une chance de nettoyer. S'ils sont toujours suspendus après cela, alorsSystemExit
c'est approprié, ou tout simplement tuer le processus à partir d'un terminal.pthread_cleanup_push()/_pop()
ce serait beaucoup de travail à implémenter correctement et ralentirait sensiblement l'interpréteur.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.
la source
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:
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.Event
et lemultiprocessing.Semaphore
travail exactement de la même manière que lethreading.Event
et 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 :
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 avecmultiprocessing
. 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()
etos.kill()
ne sont pas des choix.la source
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
la source
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 leevent
'swait()
méthode où que voussleep()
Par exemple:
Ensuite, pour l'exécuter
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 autrementsleep()
ing) et à mon avis, le code de gestion de la sortie est beaucoup plus simple .la source
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 ...
la source
Il est certainement possible d'implémenter une
Thread.stop
méthode comme indiqué dans l'exemple de code suivant:La
Thread3
classe semble exécuter le code environ 33% plus rapidement que laThread2
classe.la source
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 estsys.settrace
vraiment destiné à l'implémentation de débogueurs, de profils, etc.Thread2
classe est qu'elle exécute le code environ dix fois plus lentement. Certaines personnes pourraient toujours trouver cela acceptable.t est votre
Thread
objet.Lisez la source de python (
Modules/threadmodule.c
etPython/thread_pthread.h
) vous pouvez voirThread.ident
est unpthread_t
type, donc vous pouvez faire tout ce que vouspthread
pouvez faire en utilisation pythonlibpthread
.la source
La solution de contournement suivante peut être utilisée pour tuer un thread:
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.
la source
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:
Rendements:
la source
SystemExit
sur leafter_timeout
thread ferait quoi que ce soit au thread principal (qui attend simplement que le premier quitte dans cet exemple)?SystemExit
n'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).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:
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).
la source
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é.
la source
Bien qu'il soit plutôt ancien, cela pourrait être une solution pratique pour certains:
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.
la source
Cela semble fonctionner avec pywin32 sur Windows 7
la source
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.
la source
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:
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.la source
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:
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.
la source
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:
Il s'arrête après avoir imprimé
1
et2
.3
n'est pas imprimé.la source
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.
la source
Démarrez le sous-thread avec setDaemon (True).
la source
Voici comment procéder:
Donnez-lui quelques secondes, puis votre fil devrait être arrêté. Vérifiez également
thread._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 unequit()
méthode dans votre classe socket-handle, de terminer le socket, puis d'exécuter unthread._Thread__stop()
intérieur de votrequit()
.la source
_Thread__stop()
simplement un thread comme arrêté , il n'arrête pas réellement le thread! Ne fais jamais ça. Lisez .Si vous avez vraiment besoin de pouvoir tuer une sous-tâche, utilisez une autre implémentation.
multiprocessing
etgevent
tous 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.
la source