Python - Multithreading (Français)

Bonjour à tous, futurs sorciers Python !aujourd'hui, nous allons embarquer pour un voyage passionnant dans le monde du multithreading en Python. Ne vous inquiétez pas si vous êtes nouveau dans la programmation ; je serai votre guide amical et nous explorerons ce sujet étape par étape. Alors, prenez vos baguettes virtuelles (claviers), et plongeons-y !

Python - Multithreading

Qu'est-ce que le Multithreading ?

Avant de commencer à lancer des sorts avec des threads Python, comprenons ce qu'est le multithreading. Imaginez que vous êtes un chef dans une cuisine bondée. Si vous cuisinez seul, vous ne pouvez faire qu'une tâche à la fois – couper des légumes, puis faire bouillir de l'eau, puis frire la viande. Mais que se passe-t-il si vous aviez plusieurs mains qui pourraient faire des tâches différentes simultanément ? C'est essentiellement ce que fait le multithreading pour nos programmes !

Le multithreading permet à un programme d'exécuter plusieurs tâches de manière concurrente dans un seul processus. C'est comme avoir plusieurs chefs (threads) travaillant ensemble dans la même cuisine (processus) pour préparer un dîner délicieux (sortie du programme) plus rapidement et plus efficacement.

Comparaison avec les Processus

Maintenant, vous pourriez vous demander, "Mais monsieur, j'ai entendu parler de processus aussi. Comment les threads sont-ils différents ?" Bonne question ! Analysons cela :

  1. Utilisation des ressources : Les threads sont comme des frères et sœurs partageant une chambre (espace mémoire), tandis que les processus sont comme des voisins avec des maisons séparées. Les threads sont plus légers et partagent des ressources, ce qui les rend plus efficaces pour certaines tâches.

  2. Communication : Les threads peuvent facilement communiquer en partageant des variables, mais les processus doivent utiliser des "téléphones" spéciaux (communication inter-processus) pour se parler.

  3. Surcharge : La création et la gestion des threads sont généralement plus rapides et nécessitent moins de ressources système par rapport aux processus.

  4. Complexité : Bien que les threads puissent rendre votre programme plus rapide, ils introduisent également de la complexité. C'est comme jongler – amusant et efficace quand il est bien fait, mais vous pouvez lâcher une balle si vous n'êtes pas prudent !

Modules de Gestion des Threads en Python

Python, en tant que langage généreux qu'il est, nous fournit plusieurs modules pour travailler avec les threads. Les deux principaux sont :

  1. threading : C'est l'interface de haut niveau pour travailler avec les threads. C'est comme l'apprenti sorcier-friendly qui fait la majeure partie du travail pénible pour vous.

  2. _thread : C'est l'interface de bas niveau. C'est comme le vieux grimoire – puissant mais nécessite plus d'expertise pour être utilisé correctement.

Pour notre voyage magique aujourd'hui, nous nous concentrerons sur le module threading, car il est plus adapté aux débutants et plus largement utilisé.

Démarrer un Nouveau Thread

Très bien, lançons notre premier sort de thread ! Voici comment nous créons et démarrons un nouveau thread :

import threading
import time

def imprimer_nombres():
    for i in range(5):
        time.sleep(1)
        print(f"Thread 1: {i}")

# Créer un nouveau thread
thread1 = threading.Thread(target=imprimer_nombres)

# Démarrer le thread
thread1.start()

# Le thread principal continue d'exécuter
for i in range(5):
    time.sleep(1)
    print(f"Thread principal: {i}")

# Attendre que thread1 se termine
thread1.join()

print("Tout est terminé !")

Décomposons cette incantation magique :

  1. Nous importons les modules threading et time.
  2. Nous définissons une fonction imprimer_nombres() qui sera exécutée par notre thread.
  3. Nous créons un nouvel objet thread, en spécifiant la fonction qu'il doit exécuter.
  4. Nous démarrons le thread en utilisant la méthode start().
  5. Le thread principal continue d'exécuter son propre boucle.
  6. Nous utilisons join() pour attendre que notre thread se termine avant de terminer le programme.

Lorsque vous exécutez cela, vous verrez les nombres des deux threads entrelacés, démontrant une exécution concurrente !

Synchronisation des Threads

Maintenant, imaginez que nos aides cuisiniers essayent d'utiliser le même couteau en même temps – le chaos, non ? C'est là que la synchronisation des threads entre en jeu. Nous utilisons des verrous pour nous assurer que seul un thread peut accéder à une ressource partagée à la fois.

Voici un exemple :

import threading
import time

# Ressource partagée
compteur = 0
verrou = threading.Lock()

def incrementer_compteur():
    global compteur
    for _ in range(100000):
        verrou.acquire()
        compteur += 1
        verrou.release()

# Créer deux threads
thread1 = threading.Thread(target=incrementer_compteur)
thread2 = threading.Thread(target=incrementer_compteur)

# Démarrer les threads
thread1.start()
thread2.start()

# Attendre que les deux threads se terminent
thread1.join()
thread2.join()

print(f"Valeur finale du compteur : {compteur}")

Dans cet exemple, nous utilisons un verrou pour nous assurer que seul un thread peut incrémenter le compteur à la fois, empêchant les conditions de course.

File d'Attente à Priorité Multithreadée

Enfin, examinons une application pratique du multithreading – une file d'attente à priorité. Imaginez une salle d'urgence d'un hôpital où les patients sont traités en fonction de la gravité de leur état, et non pas juste leur heure d'arrivée.

import threading
import queue
import time
import random

# Créer une file d'attente à priorité
file_tâches = queue.PriorityQueue()

def travailleur():
    while True:
        priorité, tâche = file_tâches.get()
        print(f"Traitement de la tâche : {tâche} (Priorité : {priorité})")
        time.sleep(random.uniform(0.1, 0.5))  # Simuler le travail
        file_tâches.task_done()

# Créer et démarrer les threads travailleurs
for _ in range(3):
    thread = threading.Thread(target=travailleur, daemon=True)
    thread.start()

# Ajouter des tâches à la file
for i in range(10):
    priorité = random.randint(1, 5)
    tâche = f"Tâche {i}"
    file_tâches.put((priorité, tâche))

# Attendre que toutes les tâches soient complétées
file_tâches.join()
print("Toutes les tâches sont terminées !")

Cet exemple démontre comment plusieurs threads peuvent travailler ensemble pour traiter efficacement les tâches d'une file d'attente à priorité.

Conclusion

Félicitations, jeunes Pythonistes ! Vous avez juste pris vos premiers pas dans le royaume magique du multithreading. N'oubliez pas, avec grand pouvoir vient grande responsabilité – utilisez les threads avec sagesse, et ils rendront vos programmes plus rapides et plus efficaces.

Voici un tableau de réference rapide des principales méthodes de threading que nous avons couvertes :

Méthode Description
Thread(target=function) Crée un nouveau thread pour exécuter la fonction spécifiée
start() Démarre l'activité du thread
join() Attend que le thread se termine
Lock() Crée un verrou pour la synchronisation des threads
acquire() Acquiert un verrou
release() Libère un verrou

Continuez à pratiquer, restez curieux, et bientôt vous orchestrerez des threads comme un véritable maestro Python ! Bon codage !

Credits: Image by storyset