Module threading de Python
de PythonthreadingLe module est l'une des bibliothèques standard utilisées pour implémenter la programmation multithread. Le multithreading permet à un programme d'exécuter plusieurs tâches en même temps, améliorant ainsi l'efficacité et la réactivité du programme.
threadingLe module fournit des outils pour créer et gérer des threads, permettant aux développeurs d'écrire facilement des programmes concurrents.
Qu'est-ce qu'un thread ?
Vous pouvez considérer les threads comme des employés dans un bureau :
-
Un programme monothread est comme n'avoir qu'un seul employé, qui doit accomplir séquentiellement toutes les tâches telles que imprimer des documents, répondre aux e-mails et préparer du café.
-
Un programme multithread est comme avoir plusieurs employés, qui peuventsimultanémenteffectuer différentes tâches, améliorant grandement l'efficacité du travail.
En informatique :
- Processus: Un programme en cours d'exécution avec un espace mémoire indépendant (par exemple, le navigateur et le lecteur de musique que vous avez ouverts en même temps sont deux processus).
- Thread: Un flux d'exécution indépendant au sein d'un processus, et l'unité de base de l'ordonnancement du CPU. Tous les threads d'un même processuspartagent l'espace mémoire du processus(comme les variables globales).
Pourquoi utiliser le multithreading ?
Dans un programme monothread, les tâches sont exécutées l'une après l'autre. Si une tâche doit attendre (par exemple, attendre une réponse réseau ou une lecture de fichier), tout le programme est bloqué jusqu'à ce que la tâche se termine. Le multithreading permet au programme de continuer à exécuter d'autres tâches pendant l'attente d'une tâche, améliorant ainsi les performances globales du programme.
Threads Python et le verrou global de l'interpréteur (GIL)
Python possède un mécanisme appelé le verrou global de l'interpréteur (GIL), qui garantit qu'un seul thread peut exécuter du bytecode Python à un instant donné.
Que signifie ceci ?Pour les tâches gourmandes en CPU (comme le calcul scientifique, le traitement d'images), en raison du GIL, le multithreading ne peut généralement pas tirer parti de plusieurs cœurs pour accélérer le calcul, et peut même devenir plus lent à cause du surcoût de commutation de threads.
Alors, où le multithreading en Python est-il utile ?Pour les tâches gourmandes en E/S (comme les requêtes réseau, la lecture/écriture de fichiers, l'attente de saisie utilisateur), les threads libèrent le GIL pendant qu'ils attendent la fin des opérations d'E/S, ce qui permet aux autres threads de s'exécuter. Cela peut considérablement améliorer la réactivité et l'efficacité globales du programme, car pendant que vous attendez la réponse d'une page web, le programme peut traiter une autre tâche.
Comment utiliser le module threading ?
Pour utiliserthreadingle module, la première étape consiste à l'importer :
import threading import time # 用于模拟耗时操作
La façon la plus basique de créer un thread est d'utiliserthreading.Threadla classe.
Explication de la syntaxe :
thread_obj = threading.Thread(target=函数名, args=(参数元组,))
- target: Spécifie la fonction à exécuter après le démarrage du thread.
- args: L'argument passé à la fonction cible doit être un tuple. S'il n'y a qu'un seul argument, il doit être écrit sous
(parameter,)la forme `(arg,)`.
1. Création de threads
En Python, vous pouvez, en héritant de lathreading.Threadclasse ou en utilisant directement lethreading.Threadconstructeur, créer un thread.
Méthode 1 : Héritagethreading.ThreadClasse
Exemple
class MyThread(threading.Thread):
def run(self):
print("Le thread commence à s'exécuter")
# Écrire le code à exécuter par le thread ici
print("Exécution du thread terminée")
# Créer une instance de thread
thread = MyThread()
# Démarrer le thread
thread.start()
# Attendre que le thread se termine
thread.join()
print("Thread principal terminé")
Méthode 2 : Utilisation dethreading.Threadconstructeur.
Exemple
def my_function():
print("Le thread commence à s'exécuter")
# Écrire le code à exécuter par le thread ici
print("Exécution du thread terminée")
# Créer une instance de thread
thread = threading.Thread(target=my_function)
# Démarrer le thread
thread.start()
# Attendre que le thread se termine
thread.join()
print("Thread principal terminé")
2. Synchronisation des threads
Dans la programmation multithread, plusieurs threads peuvent accéder simultanément à des ressources partagées, ce qui peut entraîner des problèmes d'incohérence des données. Pour éviter cela, des mécanismes de synchronisation des threads tels que les verrous (Lock)。
Exemple
# Créer un objet verrou
lock = threading.Lock()
def my_function():
with lock:
print("Le thread commence à s'exécuter")
# Écrire le code à exécuter par le thread ici
print("Exécution du thread terminée")
# Créer une instance de thread
thread1 = threading.Thread(target=my_function)
thread2 = threading.Thread(target=my_function)
# Démarrer le thread
thread1.start()
thread2.start()
# Attendre que le thread se termine
thread1.join()
thread2.join()
print("Thread principal terminé")
3. Communication entre threads
La communication entre threads peut être implémentée via une file d'attente (Queue).Queueest sûr pour les threads et peut transmettre des données en toute sécurité entre plusieurs threads.
Exemple
import queue
def worker(q):
while not q.empty():
item = q.get()
print(f"Traitement de l'élément : {item}")
q.task_done()
# Créer une file d'attente et la remplir avec des données
q = queue.Queue()
for i in range(10):
q.put(i)
# Créer des instances de thread
thread1 = threading.Thread(target=worker, args=(q,))
thread2 = threading.Thread(target=worker, args=(q,))
# Démarrer les threads
thread1.start()
thread2.start()
# Attendre que tous les éléments de la file d'attente soient traités
q.join()
print("Tous les éléments traités")
Classes, méthodes et attributs courants
1. Classes principales
| Classe/Méthode/Attribut | Description | Exemple |
|---|---|---|
threading.Thread | Classe Thread, utilisée pour créer et gérer des threads | t = Thread(target=func, args=(1,)) |
threading.Lock | Verrou mutex (verrou primitif) | lock = Lock() |
threading.RLock | Verrou réentrant (peut être acquis plusieurs fois par le même thread) | rlock = RLock() |
threading.Event | Objet Event, utilisé pour la synchronisation des threads | event = Event() |
threading.Condition | Variable de condition, utilisée pour une coordination complexe des threads | cond = Condition() |
threading.Semaphore | Sémaphore, contrôle le nombre de threads simultanés | sem = Semaphore(3) |
threading.BoundedSemaphore | Sémaphore borné (empêche le compteur de dépasser la valeur initiale) | b_sem = BoundedSemaphore(2) |
threading.Timer | Thread minuteur, exécution différée | timer = Timer(5.0, func) |
threading.local | Données locales au thread (stockées indépendamment pour chaque thread) | local_data = threading.local() |
2. Méthodes/attributs courants des objets Thread
| Méthode/Attribut | Description | Exemple |
|---|---|---|
start() | Démarrer le thread | t.start() |
run() | Méthode exécutée par le thread (peut être redéfinie) | Redéfinissez cette méthode lors de la définition d'une classe personnalisée |
join(timeout=None) | Bloque le thread courant jusqu'à ce que le thread cible se termine | t.join() |
is_alive() | Vérifie si le thread est en cours d'exécution | if t.is_alive(): |
name | Nom du thread (peut être modifié) | t.name = "Worker-1" |
daemon | Indicateur de thread démon (se termine automatiquement lorsque le thread principal se termine) | t.daemon = True |
ident | Identifiant du thread (`None` avant le démarrage du thread)None) | print(t.ident) |
3. Méthodes courantes de Lock/RLock
| Méthode | Description | Exemple |
|---|---|---|
acquire(blocking=True, timeout=-1) | Acquérir le verrou (bloquant ou non bloquant) | lock.acquire() |
release() | Libérer le verrou | lock.release() |
locked() | Vérifier si le verrou est détenu | if not lock.locked(): |
4. Méthodes courantes de Event
| Méthode | Description | Exemple |
|---|---|---|
set() | Définir l'événement sur true et réveiller tous les threads en attente | event.set() |
clear() | Réinitialiser l'événement sur false | event.clear() |
wait(timeout=None) | Bloquer jusqu'à ce que l'événement soit true ou qu'un délai expire | event.wait(2.0) |
is_set() | Vérifier l'état de l'événement | if event.is_set(): |
5. Méthodes courantes de Condition
| Méthode | Description | Exemple |
|---|---|---|
wait(timeout=None) | Libérer le verrou et bloquer jusqu'à être notifié ou qu'un délai expire | cond.wait() |
notify(n=1) | Réveiller au plusnthreads en attente | cond.notify(2) |
notify_all() | Réveiller tous les threads en attente | cond.notify_all() |
6. Fonctions/attributs au niveau du module
| Fonction/Attribut | Description | Exemple |
|---|---|---|
threading.active_count() | Retourne le nombre de threads actuellement actifs | print(threading.active_count()) |
threading.current_thread() | Retourne l’objet du thread actuel | print(threading.current_thread().name) |
threading.enumerate() | Retourne une liste de tous les threads actifs | for t in threading.enumerate(): |
threading.main_thread() | Retourne l’objet du thread principal | if threading.current_thread() is threading.main_thread(): |
threading.get_ident() | Retourne l’identifiant du thread actuel (Python 3.3+) | print(threading.get_ident()) |
Exemple
1. Création de threads de base
Exemple
def worker(num):
print(f"Worker {num} started")
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
2. Utilisation de verrous pour protéger les ressources partagées
Exemple
count = 0
def increment():
global count
with lock: # Acquiert et libère automatiquement le verrou
count += 1
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(count) # Sortie : 10
3. Synchronisation d'événements
Exemple
def waiter():
print("En attente de l'événement...")
event.wait()
print("Événement déclenché !")
t = threading.Thread(target=waiter)
t.start()
# Le thread principal déclenche l'événement
threading.Event().wait(2.0) # Délai simulé
event.set()
t.join()
4. Modèle producteur-consommateur (Condition)
Exemple
from threading import Condition
queue = []
cond = Condition()
MAX_ITEMS = 5
def producer():
for _ in range(10):
with cond:
while len(queue) >= MAX_ITEMS:
cond.wait()
item = random.randint(1, 100)
queue.append(item)
print(f"Produced {item}")
cond.notify()
def consumer():
for _ in range(10):
with cond:
while not queue:
cond.wait()
item = queue.pop(0)
print(f"Consumed {item}")
cond.notify()
threading.Thread(target=producer).start()
threading.Thread(target=consumer).start()
Notes
Verrou global de l'interpréteur (GIL): Le GIL de Python ne permet qu'à un seul thread d'exécuter du bytecode Python à la fois. Par conséquent, dans les tâches gourmandes en CPU, le multithreading peut ne pas apporter d'améliorations de performances. Pour les tâches gourmandes en E/S, le multithreading reste bénéfique.
Sécurité des threads: Dans un environnement multithread, assurez-vous que l'accès aux ressources partagées est thread-safe, en évitant les courses de données et les interblocages.
Nombre de threads: Créer trop de threads peut épuiser les ressources système et affecter les performances du programme. Contrôlez raisonnablement le nombre de threads, ou utilisez un pool de threads (
ThreadPoolExecutor) pour gérer les threads.