Introduction
Quand on développe une application Python, il peut être utile d’exécuter plusieurs tâches en même temps. Pour cela, Python propose deux techniques principales : les threads (avec le module threading
) et les processus (avec multiprocessing
). Les threads permettent d’exécuter plusieurs fonctions simultanément au sein du même processus, partageant la même mémoire, tandis que les processus sont totalement indépendants, chacun avec sa propre mémoire.
Mais alors, quand utiliser l’un ou l’autre ? Et comment créer un thread en Python ? Cet article vous propose une introduction pratique à threading
, et une comparaison avec multiprocessing
pour bien comprendre les avantages et les limites de chaque approche.
1. Qu’est-ce qu’un thread ?
Un thread (ou fil d’exécution) est une unité d’exécution légère, qui s'exécute en parallèle à d'autres threads dans le même processus. Tous les threads partagent la mémoire et les ressources du programme principal.
Avantages :
- Partage de la mémoire (communication facile entre threads)
- Léger et rapide à créer
- Idéal pour les tâches I/O (réseau, fichiers…)
Inconvénients :
- GIL (Global Interpreter Lock) empêche les threads Python purs d’être exécutés en vrai parallèle sur plusieurs cœurs pour les tâches CPU.
2. Créer un thread en Python
import threading
def afficher():
print("Thread exécuté")
thread = threading.Thread(target=afficher)
thread.start()
thread.join()
start()
lance le thread.join()
attend la fin du thread.
3. Lancer plusieurs threads
def travail(n):
print(f"Thread {n} en cours")
threads = []
for i in range(5):
t = threading.Thread(target=travail, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
4. Utiliser une classe personnalisée pour les threads
Vous pouvez hériter de la classe Thread
pour créer des threads personnalisés :
class MonThread(threading.Thread):
def __init__(self, nom):
super().__init__()
self.nom = nom
def run(self):
print(f"Thread {self.nom} est lancé")
t1 = MonThread("A")
t2 = MonThread("B")
t1.start()
t2.start()
t1.join()
t2.join()
5. Partage de ressources et synchronisation
Problème classique : plusieurs threads accèdent à une même variable
compteur = 0
def incrementer():
global compteur
for _ in range(100000):
compteur += 1
t1 = threading.Thread(target=incrementer)
t2 = threading.Thread(target=incrementer)
t1.start()
t2.start()
t1.join()
t2.join()
print("Compteur =", compteur)
➡ Résultat incohérent à cause de conditions de course.
6. Utiliser un verrou (Lock
) pour synchroniser les threads
verrou = threading.Lock()
compteur = 0
def incrementer():
global compteur
for _ in range(100000):
with verrou:
compteur += 1
t1 = threading.Thread(target=incrementer)
t2 = threading.Thread(target=incrementer)
t1.start()
t2.start()
t1.join()
t2.join()
print("Compteur synchronisé =", compteur)
7. Différences entre threading et multiprocessing
Aspect | Threading | Multiprocessing |
---|---|---|
Type | Plusieurs threads dans 1 processus | Plusieurs processus séparés |
Partage mémoire | Oui | Non |
Véritable parallèle | Non (à cause du GIL) | Oui |
Usage recommandé | I/O bound (réseau, fichiers...) | CPU bound (calculs lourds) |
Communication | Simple (même mémoire) | Complexe (via Queue, Pipe, etc.) |
8. Quand utiliser threading ?
Utilisez threading
lorsque :
- Vous traitez plusieurs tâches d'attente (I/O)
- Vous avez besoin de réactivité (ex. interfaces graphiques)
- Vous n’avez pas besoin de calculs lourds
9. Exemples concrets d'utilisation de threading
Téléchargement de plusieurs fichiers en parallèle :
import requests
def telecharger(url):
print(f"Téléchargement de {url}")
r = requests.get(url)
print(f"{url} terminé ({len(r.content)} octets)")
urls = ["https://www.example.com", "https://www.python.org"]
threads = [threading.Thread(target=telecharger, args=(u,)) for u in urls]
for t in threads: t.start()
for t in threads: t.join()
10. Limites de threading en Python
- Le GIL empêche l'exécution simultanée de plusieurs threads sur plusieurs cœurs pour les calculs purs.
- Attention aux bugs difficiles à reproduire à cause des accès concurrents.
- Privilégiez
multiprocessing
pour le traitement intensif.
Conclusion
Le module threading
de Python est un outil puissant pour lancer plusieurs tâches concurrentes dans le même processus. Bien qu’il ne permette pas le vrai parallélisme CPU à cause du GIL, il reste très efficace pour les tâches d’entrées/sorties, comme le téléchargement, les appels API ou la gestion d’interfaces utilisateurs.
Savoir quand utiliser threading
ou multiprocessing
est essentiel pour écrire des programmes performants et bien structurés.