Implémentation de threads

Dans ce chapitre, nous allons apprendre à implémenter des threads en Python.

Module Python pour l'implémentation des threads

Les threads Python sont parfois appelés processus légers car les threads occupent beaucoup moins de mémoire que les processus. Les threads permettent d'effectuer plusieurs tâches à la fois. En Python, nous avons les deux modules suivants qui implémentent des threads dans un programme -

  • <_thread>module

  • <threading>module

La principale différence entre ces deux modules est que <_thread> le module traite un thread comme une fonction alors que le module <threading>module traite chaque thread comme un objet et l'implémente de manière orientée objet. De plus, le<_thread>module est efficace dans le threading de bas niveau et a moins de capacités que le <threading> module.

Module <_thread>

Dans la version précédente de Python, nous avions le <thread>module mais il a été considéré comme "obsolète" pendant assez longtemps. Les utilisateurs ont été encouragés à utiliser le<threading>module à la place. Par conséquent, dans Python 3, le module "thread" n'est plus disponible. Il a été renommé en "<_thread>"pour les incompatibilités vers l'arrière dans Python3.

Pour générer un nouveau fil à l'aide du <_thread> module, nous devons appeler le start_new_threadméthode de celui-ci. Le fonctionnement de cette méthode peut être compris à l'aide de la syntaxe suivante -

_thread.start_new_thread ( function, args[, kwargs] )

Ici -

  • args est un tuple d'arguments

  • kwargs est un dictionnaire facultatif d'arguments de mots clés

Si nous voulons appeler une fonction sans passer d'argument, nous devons utiliser un tuple vide d'arguments dans args.

Cet appel de méthode retourne immédiatement, le thread enfant démarre et appelle la fonction avec la liste passée, le cas échéant, d'arguments. Le thread se termine au fur et à mesure que la fonction retourne.

Exemple

Voici un exemple de génération d'un nouveau thread à l'aide de <_thread>module. Nous utilisons ici la méthode start_new_thread ().

import _thread
import time

def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

try:
   _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print ("Error: unable to start thread")
while 1:
   pass

Production

La sortie suivante nous aidera à comprendre la génération de nouveaux threads à l'aide du <_thread> module.

Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018

Module <threading>

le <threading>module implémente de manière orientée objet et traite chaque thread comme un objet. Par conséquent, il fournit une prise en charge beaucoup plus puissante et de haut niveau des threads que le module <_thread>. Ce module est inclus avec Python 2.4.

Méthodes supplémentaires dans le module <threading>

le <threading> module comprend toutes les méthodes du <_thread>module mais il fournit également des méthodes supplémentaires. Les méthodes supplémentaires sont les suivantes -

  • threading.activeCount() - Cette méthode renvoie le nombre d'objets thread qui sont actifs

  • threading.currentThread() - Cette méthode renvoie le nombre d'objets de thread dans le contrôle de thread de l'appelant.

  • threading.enumerate() - Cette méthode renvoie une liste de tous les objets de thread actuellement actifs.

  • Pour implémenter le threading, le <threading> module a le Thread classe qui fournit les méthodes suivantes -

    • run() - La méthode run () est le point d'entrée d'un thread.

    • start() - La méthode start () démarre un thread en appelant la méthode run.

    • join([time]) - Le join () attend la fin des threads.

    • isAlive() - La méthode isAlive () vérifie si un thread est toujours en cours d'exécution.

    • getName() - La méthode getName () renvoie le nom d'un thread.

    • setName() - La méthode setName () définit le nom d'un thread.

Comment créer des threads à l'aide du module <threading>?

Dans cette section, nous allons apprendre à créer des threads en utilisant le <threading>module. Suivez ces étapes pour créer un nouveau thread à l'aide du module <threading> -

  • Step 1 - Dans cette étape, nous devons définir une nouvelle sous-classe du Thread classe.

  • Step 2 - Ensuite, pour ajouter des arguments supplémentaires, nous devons remplacer le __init__(self [,args]) méthode.

  • Step 3 - Dans cette étape, nous devons remplacer la méthode run (self [, args]) pour implémenter ce que le thread doit faire au démarrage.

  • Maintenant, après avoir créé le nouveau Thread sous-classe, nous pouvons en créer une instance, puis démarrer un nouveau thread en invoquant le start(), qui à son tour appelle le run() méthode.

Exemple

Considérez cet exemple pour apprendre à générer un nouveau thread en utilisant le <threading> module.

import threading
import time
exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
   def run(self):
      print ("Starting " + self.name)
      print_time(self.name, self.counter, 5)
      print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
   while counter:
      if exitFlag:
         threadName.exit()
      time.sleep(delay)
      print ("%s: %s" % (threadName, time.ctime(time.time())))
      counter -= 1

thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Starting Thread-1
Starting Thread-2

Production

Maintenant, considérez la sortie suivante -

Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread

Programme Python pour divers états de thread

Il existe cinq états de thread: nouveau, exécutable, en cours d'exécution, en attente et mort. Parmi ces cinq de ces cinq, nous nous concentrerons principalement sur trois états - en cours d'exécution, en attente et mort. Un thread obtient ses ressources dans l'état d'exécution, attend les ressources dans l'état d'attente; la version finale de la ressource, si elle est en cours d’exécution et acquise, est à l’état mort.

Le programme Python suivant à l'aide des méthodes start (), sleep () et join () montrera comment un thread est entré en état d'exécution, d'attente et mort respectivement.

Step 1 - Importez les modules nécessaires, <threading> et <time>

import threading
import time

Step 2 - Définissez une fonction qui sera appelée lors de la création d'un thread.

def thread_states():
   print("Thread entered in running state")

Step 3 - Nous utilisons la méthode sleep () du module de temps pour faire attendre notre thread pendant 2 secondes.

time.sleep(2)

Step 4 - Maintenant, nous créons un thread nommé T1, qui prend l'argument de la fonction définie ci-dessus.

T1 = threading.Thread(target=thread_states)

Step 5- Maintenant, avec l'aide de la fonction start (), nous pouvons démarrer notre thread. Il produira le message que nous avons défini lors de la définition de la fonction.

T1.start()
Thread entered in running state

Step 6 - Maintenant, nous pouvons enfin tuer le thread avec la méthode join () une fois son exécution terminée.

T1.join()

Démarrer un thread en Python

En python, nous pouvons démarrer un nouveau thread de différentes manières, mais la plus simple d'entre elles est de le définir comme une fonction unique. Après avoir défini la fonction, nous pouvons la passer comme cible pour un nouveauthreading.Threadobjet et ainsi de suite. Exécutez le code Python suivant pour comprendre le fonctionnement de la fonction -

import threading
import time
import random
def Thread_execution(i):
   print("Execution of Thread {} started\n".format(i))
   sleepTime = random.randint(1,4)
   time.sleep(sleepTime)
   print("Execution of Thread {} finished".format(i))
for i in range(4):
   thread = threading.Thread(target=Thread_execution, args=(i,))
   thread.start()
   print("Active Threads:" , threading.enumerate())

Production

Execution of Thread 0 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>]

Execution of Thread 1 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>]

Execution of Thread 2 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>]

Execution of Thread 3 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>,
      <Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished

Fils de démon en Python

Avant d'implémenter les threads de démon en Python, nous devons connaître les threads de démon et leur utilisation. En termes de calcul, le démon est un processus d'arrière-plan qui gère les demandes de divers services tels que l'envoi de données, les transferts de fichiers, etc. Il serait inactif s'il n'est plus nécessaire. La même tâche peut également être effectuée avec l'aide de threads non démon. Cependant, dans ce cas, le thread principal doit suivre manuellement les threads non démon. D'un autre côté, si nous utilisons des threads démons, le thread principal peut complètement oublier cela et il sera tué à la fermeture du thread principal. Un autre point important à propos des threads démons est que nous pouvons choisir de les utiliser uniquement pour des tâches non essentielles qui ne nous affecteraient pas si elles ne se terminent pas ou se font tuer entre les deux. Voici l'implémentation des threads démons en python -

import threading
import time

def nondaemonThread():
   print("starting my thread")
   time.sleep(8)
   print("ending my thread")
def daemonThread():
   while True:
   print("Hello")
   time.sleep(2)
if __name__ == '__main__':
   nondaemonThread = threading.Thread(target = nondaemonThread)
   daemonThread = threading.Thread(target = daemonThread)
   daemonThread.setDaemon(True)
   daemonThread.start()
   nondaemonThread.start()

Dans le code ci-dessus, il y a deux fonctions à savoir >nondaemonThread() et >daemonThread(). La première fonction imprime son état et se met en veille après 8 secondes tandis que la fonction deamonThread () imprime indéfiniment Hello toutes les 2 secondes. Nous pouvons comprendre la différence entre les threads nondaemon et daemon à l'aide de la sortie suivante -

Hello

starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello