Python >> Tutoriel Python >  >> Python

Comment tuer un thread en Python ?

Résumé : Pour tuer un thread, utilisez l'une des méthodes suivantes :

  • Créer une Exit_Request drapeau.
  • Utiliser le multiprocessing module.
  • Utiliser le trace module.
  • Utiliser ctypes pour lever des exceptions dans un thread

Problème : Comment tuer un thread en Python ?

C'est l'une des questions les plus fréquemment posées en Python. Ainsi, dans cet article, nous allons aborder ce problème et nous assurer que vous avez une compréhension claire de ce sujet. Avant de plonger directement dans le sujet, nous examinerons le fil en bref, puis nous passerons à notre question essentielle à la mission. Alors sans plus tarder, que les jeux commencent ! ?

Présentation

Qu'est-ce qu'un fil ?

Selon Wikipedia, en informatique, un thread d'exécution est la plus petite séquence d'instructions programmées pouvant être gérée indépendamment par un ordonnanceur, qui fait généralement partie du système d'exploitation.

➽ Plusieurs threads peuvent s'exécuter simultanément dans un même programme. Par exemple, dans un traitement de texte, un thread d'arrière-plan peut être utilisé pour la vérification orthographique tandis qu'un thread de premier plan peut être utilisé pour traiter l'entrée de l'utilisateur (frappes au clavier), tandis qu'un troisième thread peut être utilisé pour charger des images à partir du disque dur, et un le quatrième thread peut être occupé à effectuer des sauvegardes automatiques périodiques du fichier en cours d'édition.

  • Par conséquent, le threading en Python est utilisé pour exécuter plusieurs threads qui peuvent être des tâches ou des appels de fonction, en même temps. Veuillez noter que cela ne signifie pas que plusieurs threads sont exécutés sur différents processeurs.
  • Les threads Python ne rendent pas le programme plus rapide s'il utilise déjà 100 % du temps CPU. Cela fait probablement partie de la programmation parallèle qui n'entre pas dans le cadre de cette discussion.

Remarque : Le threading en Python est lui-même un sujet de discussion à part entière. Le but de cette introduction est simplement de vous familiariser avec l'idée des threads. Il est fortement recommandé d'avoir une certaine compréhension du threading en Python avant de plonger dans le sujet de la suppression du thread.

Quand utilisons-nous Threading en Python ?

Les threads en Python sont utilisés dans des situations où l'exécution d'un appel de tâche ou de fonction implique une certaine attente. Un exemple pourrait être l'interaction avec un service hébergé sur un serveur Web. Ainsi, le threading permet à Python d'exécuter d'autres codes en attendant.

Exemple :

import threading
import time

print("Printing values from 90 to 100: ")
def loop_thread():
    for t in range(90, 101):
        time.sleep(1)
        print(t)

threading.Thread(target=loop_thread).start()

Sortie :

Printing values from 90 to 100: 
90
91
92
93
94
95
96
97
98
99
100

Maintenant que vous avez un aperçu des fils de discussion, plongeons-nous dans notre question essentielle.

Méthodes pour tuer un thread en Python

En termes simples, tuer un fil brusquement est une très mauvaise pratique, sauf si cela est absolument nécessaire . Cela dit, vous voudrez peut-être tuer un thread après un certain temps ou après une interruption. Ainsi, au lieu de le fermer de force, vous pouvez gentiment demander au fil de disparaître après s'être assuré d'un bon nettoyage, ce qui signifie :

  • Le thread que vous essayez de tuer contient peut-être une ressource critique qui doit être fermée correctement.
  • Le thread peut avoir créé plusieurs autres threads qui doivent également être tués avant que le thread parent puisse être tué.

Comprenons les nombreuses façons qui nous aident à le faire.

Méthode 1 :Création d'un indicateur de demande de sortie

Si vous êtes le gestionnaire de vos propres threads, une bonne façon de gérer la terminaison des threads consiste à créer un indicateur de sortie que chaque thread vérifie à intervalles réguliers pour savoir s'il est temps pour lui de se terminer.

Exemple :

import threading 
import time 
  
def thread_foo(stop):
    while True: 
        print("THREAD STILL RUNNING!") 
        if exit_flag: 
            break

exit_flag = False
t = threading.Thread(target = thread_foo, args =(lambda : exit_flag, )) 
t.start() 
time.sleep(0.1) 
print('Done sleeping! Time to stop the threads.')
exit_flag = True
t.join() 
print('THREAD TERMINATED!')

Sortie :

Dans l'exemple ci-dessus, nous avons créé une fonction qui continue d'exécuter le thread jusqu'à ce que le programme rencontre la variable exit_flag = True . Dès que cela se produit, le thread peut être tué en utilisant le t.join() méthode.

Remarque : join() oblige le thread principal à attendre que votre thread termine son exécution. En d'autres termes, join() agit comme un "hold" sur le thread principal. Il garantit que votre thread a terminé son exécution avant que le thread principal ne puisse avancer. D'une certaine manière, cela assure une terminaison propre et correcte de votre fil.

Méthode 2 :Utilisation du module de multitraitement

Comme indiqué dans la documentation Python, multiprocessing est un package qui prend en charge les processus de génération à l'aide d'une API similaire au threading module. De plus, tuer un processus est beaucoup plus sûr que de tuer un thread car les threads partagent des variables globales et ont une dépendance, alors que les processus sont complètement indépendants et existent séparément les uns des autres. Le multiprocessing module a le terminate() fonction utilisée pour tuer un processus.

Jetons un coup d'œil à l'exemple suivant donné ci-dessous pour comprendre comment nous pouvons utiliser le multiprocessing module pour tuer le processus.

import multiprocessing
import time
def Child_process():
  while True:
   for i in range (20):  
    print ('Process: ', i)
    time.sleep(0.05)
t = multiprocessing.Process(target = Child_process)
t.start()
time.sleep(0.5)
t.terminate()
print("Child Process successfully terminated")

Sortie :

Process:  0
Process:  1
Process:  2
Process:  3
Process:  4
Process:  5
Process:  6
Process:  7
Process:  8
Process:  9
Child Process successfully terminated

Dans l'exemple ci-dessus après 0,5 seconde l'exécution du Child_process a été terminé en utilisant le terminate() fonction.

Méthode 3 :Utilisation du module de trace

Une autre approche pour tuer un thread consiste à installer trace dans le thread qui sortira du thread.

Le module donné ci-dessous vous permet de tuer les threads. La classe KThread est un remplacement direct pour threading.Thread . Il ajoute le kill() méthode, qui devrait arrêter la plupart des threads dans leur élan.

Avis de non-responsabilité : La procédure ci-dessous est tirée de la ressource suivante :Kill a thread in Python

KThread.py :Une implémentation Thread tuable

import time
import sys
import trace
import threading

class KThread(threading.Thread):
  """A subclass of threading.Thread, with a kill()
method."""
  def __init__(self, *args, **keywords):
    threading.Thread.__init__(self, *args, **keywords)
    self.killed = False

  def start(self):
    """Start the thread."""
    self.__run_backup = self.run
    self.run = self.__run     
    threading.Thread.start(self)

  def __run(self):
    """Hacked run function, which installs the
trace."""
    sys.settrace(self.globaltrace)
    self.__run_backup()
    self.run = self.__run_backup

  def globaltrace(self, frame, why, arg):
    if why == 'call':
      return self.localtrace
    else:
      return None

  def localtrace(self, frame, why, arg):
    if self.killed:
      if why == 'line':
        raise SystemExit()
    return self.localtrace

  def kill(self):
    self.killed = True


# Example Usage
#This illustrates running a function in a separate thread. The thread is killed before the function finishes.
def func():
  print('Function started')
  for i in range(1,100):
    print(i)
    time.sleep(0.2)
  print('Function finished')

A = KThread(target=func)
A.start()
time.sleep(1)
A.kill()

Sortie :

Function started
1
2
3
4
5

Méthode 4 :Utiliser des ctypes pour générer des exceptions dans un fil

Dans les cas où vous devez tuer un thread de force, par exemple, lors de l'encapsulation d'une bibliothèque externe occupée par de longs appels et que vous souhaitez l'interrompre, vous pouvez utiliser raise_exc() pour lever une exception arbitraire, ou appelez terminate() pour lever SystemExit automatiquement. Il utilise le PyThreadState_SetAsyncExc non exposé fonction (via ctypes) pour lever une exception dans le contexte du thread donné.

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

Exemple :

import threading
import inspect
import ctypes
import time


def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    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(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")


class Thread(threading.Thread):
    def _get_my_tid(self):
        """determines this (self's) thread id"""
        if not self.is_alive():
            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
        
        raise AssertionError("could not determine the thread's id")
    
    def raise_exc(self, exctype):
        """raises the given exception type in the context of this thread"""
        _async_raise(self._get_my_tid(), exctype)
    
    def terminate(self):
        """raises SystemExit in the context of the given thread, which should 
        cause the thread to exit silently (unless caught)"""
        self.raise_exc(SystemExit)

def f():
     try:
         while True:
            for i in range(10000):
             print(i) 
             time.sleep(0.1)
     finally:
         print ("TERMINATING!!!")

t = Thread(target = f)
t.start()
time.sleep(0.6)
t.terminate()
t.join()

Sortie :

0
1
2
3
4
5
TERMINATING!!!

Conclusion

Les principaux points à retenir de cet article étaient :

  • Qu'est-ce qu'un fil ?
  • Méthodes pour tuer un thread en Python :
    • Création d'un indicateur Exit_Request.
    • Utiliser le Multiprocessing module.
    • Utiliser le trace module.
    • Utiliser ctypes pour lever des exceptions dans un thread

Veuillez vous abonner et restez à l'écoute pour des articles plus intéressants !

Références

  1. https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread
  2. http://net-informations.com/python/iq/kill.htm
  3. https://pybay.com/site_media/slides/raymond2017-keynote/threading.html
  4. http://tomerfiliba.com/recipes/Thread2/

Humour nerd