Python - Synchronisation des Threads
Bonjour à tous, futurs sorciers Python !aujourd'hui, nous allons entamer un voyage passionnant dans le monde de la synchronisation des threads. Imaginez que vous dirigez un orchestre où chaque musicien est un thread, et que vous devez vous assurer qu'ils jouent tous en harmonie. C'est essentiellement ce qu'est la synchronisation des threads en programmation !
Synchronisation des Threads à l'Aide de Verrous
Commençons par l'outil le plus basique de notre boîte à outils de synchronisation : les verrous. Pensez à un verrou comme à un panneau "ne pas déranger" sur la porte d'une chambre d'hôtel. Lorsqu'un thread acquiert un verrou, c'est comme mettre ce panneau, en disant aux autres threads, "Eh, je suis occupé ici !"
Voici un exemple simple pour illustrer ce concept :
import threading
import time
# Ressource partagée
compteur = 0
# Créer un verrou
verrou = threading.Lock()
def incrementer():
global compteur
for _ in range(100000):
verrou.acquire()
compteur += 1
verrou.release()
# Créer deux threads
thread1 = threading.Thread(target=incrementer)
thread2 = threading.Thread(target=incrementer)
# Démarrer les threads
thread1.start()
thread2.start()
# Attendre que les threads se terminent
thread1.join()
thread2.join()
print(f"Valeur finale du compteur : {compteur}")
Dans cet exemple, nous avons une ressource partagée compteur
que deux threads essaient d'incrémenter. Sans le verrou, nous pourrions avoir une condition de course, où les deux threads essaient d'incrémenter le compteur simultanément, potentially leading to incorrect results.
En utilisant verrou.acquire()
avant de modifier le compteur et verrou.release()
après, nous nous assurons que seul un thread peut incrémenter le compteur à la fois. C'est comme passer un témoin dans une course de relais – seul le thread tenant le témoin (verrou) peut courir (modifier la ressource partagée).
Objets Condition pour la Synchronisation des Threads Python
Maintenant, augmentons notre jeu de synchronisation avec les objets Condition. Ce sont comme des feux de circulation sophistiqués pour nos threads, permettant une coordination plus complexe.
Voici un exemple de scénario producteur-consommateur utilisant un objet Condition :
import threading
import time
import random
# Tampon partagé
tampon = []
MAX_SIZE = 5
# Créer un objet condition
condition = threading.Condition()
def producteur():
global tampon
while True:
with condition:
while len(tampon) == MAX_SIZE:
print("Tampon plein, le producteur attend...")
condition.wait()
item = random.randint(1, 100)
tampon.append(item)
print(f"Produit : {item}")
condition.notify()
time.sleep(random.random())
def consommateur():
global tampon
while True:
with condition:
while len(tampon) == 0:
print("Tampon vide, le consommateur attend...")
condition.wait()
item = tampon.pop(0)
print(f"Consommé : {item}")
condition.notify()
time.sleep(random.random())
# Créer les threads producteur et consommateur
producteur_thread = threading.Thread(target=producteur)
consommateur_thread = threading.Thread(target=consommateur)
# Démarrer les threads
producteur_thread.start()
consommateur_thread.start()
# Laisser le programme s'exécuter pendant un moment
time.sleep(10)
Dans cet exemple, nous avons un producteur ajoutant des éléments à un tampon et un consommateur enlevant des éléments de ce tampon. L'objet Condition aide à coordonner leurs actions :
- Le producteur attend lorsque le tampon est plein.
- Le consommateur attend lorsque le tampon est vide.
- Ils s'informent mutuellement lorsque c'est sûr de procéder.
C'est comme une danse bien chorégraphiée, avec l'objet Condition en tant que chorégraphe !
Synchronisation des Threads à l'Aide de la Méthode join()
La méthode join()
est comme dire à un thread d'attendre que l'autre termine sa performance avant de monter sur scène. C'est une manière simple mais puissante de synchroniser les threads.
Voici un exemple :
import threading
import time
def ouvrier(name, delay):
print(f"{name} commence...")
time.sleep(delay)
print(f"{name} a terminé!")
# Créer des threads
thread1 = threading.Thread(target=ouvrier, args=("Thread 1", 2))
thread2 = threading.Thread(target=ouvrier, args=("Thread 2", 4))
# Démarrer les threads
thread1.start()
thread2.start()
# Attendre que thread1 se termine
thread1.join()
print("Thread principal attend après thread1")
# Attendre que thread2 se termine
thread2.join()
print("Thread principal attend après thread2")
print("Tous les threads ont terminé!")
Dans cet exemple, le thread principal démarre deux threads ouvriers et attend ensuite que chacun termine en utilisant join()
. C'est comme un parent attendant que ses enfants terminent leurs devoirs avant de servir le dîner !
Primitives de Synchronisation Supplémentaires
Python offre plusieurs autres outils pour la synchronisation des threads. Jetons un coup d'œil rapide à certains d'entre eux :
Primitive | Description | Cas d'Utilisation |
---|---|---|
Semaphore | Permet à un nombre limité de threads d'accéder à une ressource | Gérer un pool de connexions à base de données |
Event | Permet à un thread de signaler un événement à d'autres threads | Signaler que une tâche est terminée |
Barrier | Permet à plusieurs threads d'attendre jusqu'à ce qu'ils atteignent un certain point | Synchroniser le début d'une course |
Voici un exemple rapide utilisant un Semaphore :
import threading
import time
# Créer un semaphore qui permet 2 threads à la fois
semaphore = threading.Semaphore(2)
def ouvrier(name):
with semaphore:
print(f"{name} a acquis le semaphore")
time.sleep(1)
print(f"{name} a libéré le semaphore")
# Créer et démarrer 5 threads
threads = []
for i in range(5):
thread = threading.Thread(target=ouvrier, args=(f"Thread {i}",))
threads.append(thread)
thread.start()
# Attendre que tous les threads se terminent
for thread in threads:
thread.join()
print("Tous les threads ont terminé!")
Dans cet exemple, le semaphore agit comme un videur dans un club, ne permettant qu'à deux threads d'entrer à la fois. Il est parfait pour les situations où vous devez limiter l'accès à une ressource rare !
Et voilà, mes amis ! Nous avons exploré le monde fascinant de la synchronisation des threads en Python. N'oubliez pas, comme diriger un orchestre ou chorégraphier une danse, synchroniser des threads, c'est tout une question de coordination et de timing. Avec ces outils dans votre boîte à outils de programmation, vous êtes bien parti pour créer des programmes Python multithreadés harmonieux. Continuez à pratiquer, soyez curieux et bon codage !
Credits: Image by storyset