Dans ce tutoriel, vous apprendrez à utiliser le module de threading intégré à Python pour explorer les capacités de multithreading de Python.
En commençant par les bases des processus et des threads, vous apprendrez comment fonctionne le multithreading en Python, tout en comprenant les concepts de concurrence et de parallélisme. Vous apprendrez ensuite à lancer et à exécuter un ou plusieurs threads en Python à l'aide du module de threading
intégré.
Commençons.
Processus et threads : Différences
Qu'est-ce qu'un processus ?
Un processus est une instance d'un programme qui doit s'exécuter.
Il peut s'agir de n'importe quoi, d'un script Python, d'un navigateur web tel que Chrome ou d'une application de vidéoconférence. Si vous lancez le Gestionnaire des tâches sur votre machine et que vous naviguez vers Performance –> UNITÉ CENTRALE, vous pourrez voir les processus et les threads qui sont actuellement en cours d'exécution sur les cœurs de votre CPU.

Comprendre les processus et les fils
En interne, un processus dispose d'une mémoire dédiée qui stocke le code et les données correspondant au processus.
Un processus se compose d'un ou de plusieurs fils. Un thread est la plus petite séquence d'instructions que le système d'exploitation peut exécuter et il représente le flux d'exécution.
Chaque fil dispose de sa propre pile et de ses propres registres, mais pas d' une mémoire dédiée. Tous les threads associés à un processus peuvent accéder aux données. Les données et la mémoire sont donc partagées par tous les threads d'un processus.

Dans une unité centrale dotée de N cœurs, N processus peuvent s'exécuter en parallèle au même moment. Cependant, deux fils d'un même processus ne peuvent jamais s'exécuter en parallèle, mais peuvent s'exécuter simultanément. Nous aborderons le concept de concurrence et de parallélisme dans la section suivante.
Sur la base de ce que nous avons appris jusqu'à présent, résumons les différences entre un processus et un thread.
Caractéristique | Processus | Fil de discussion |
Mémoire | Mémoire dédiée | Mémoire partagée |
Mode d'exécution | Parallèle, concurrent | Concurrent, mais pas parallèle |
Exécution gérée par | Le système d'exploitation | Interprète CPython |
Multithreading en Python
En Python, le verrou global de l'interprète (GIL) garantit qu'un seul thread peut acquérir le verrou et s'exécuter à tout moment. Tous les threads doivent acquérir ce verrou pour s'exécuter. Cela garantit qu'un seul thread peut être en cours d'exécution - à un moment donné - et évite le multithreading simultané.
Prenons l'exemple de deux fils, t1
et t2
, d'un même processus. Comme les threads partagent les mêmes données, lorsque t1
lit une valeur particulière k
, t2
peut modifier la même valeur k
. Cela peut entraîner des blocages et des résultats indésirables. Mais un seul des threads peut acquérir le verrou et s'exécuter à tout moment. Par conséquent, la GIL garantit également la sécurité des threadss.
Alors, comment obtenir des capacités de multithreading en Python ? Pour le comprendre, examinons les concepts de simultanéité et de parallélisme.
Concurrence et parallélisme : Vue d'ensemble
Prenons l'exemple d'une unité centrale dotée de plusieurs cœurs. Dans l'illustration ci-dessous, l'unité centrale possède quatre cœurs. Cela signifie que quatre opérations différentes peuvent être exécutées en parallèle à tout moment.
S'il y a quatre processus, chacun d'entre eux peut s'exécuter indépendamment et simultanément sur chacun des quatre cœurs. Supposons que chaque processus dispose de deux threads.

Pour comprendre le fonctionnement du threading, passons d'une architecture de processeur multicœur à une architecture de processeur monocœur. Comme nous l'avons vu, un seul thread peut être actif à un moment donné de l'exécution, mais le cœur du processeur peut passer d'un thread à l'autre.

Par exemple, les threads liés aux E/S attendent souvent les opérations d'E/S : lecture des entrées utilisateur, lecture des bases de données et opérations sur les fichiers. Pendant ce temps d'attente, le thread lié aux E/S peut libérer le verrou pour que l'autre thread puisse s'exécuter. Le temps d'attente peut également être une simple opération telle que dormir pendant n
seconde.
En résumé : pendant les opérations d'attente, le thread libère le verrou, ce qui permet au cœur du processeur de passer à un autre thread. Le thread précédent reprend l'exécution à la fin de la période d'attente. Ce processus, au cours duquel le cœur du processeur passe d'un thread à l'autre simultanément, facilite le multithreading. ✅
Si vous souhaitez mettre en œuvre un parallélisme au niveau du processus dans votre application, envisagez plutôt d'utiliser le multiprocessing.
Module de threading de Python : Premiers pas
Python est livré avec un module de threading
que vous pouvez importer dans le script Python.
import threading
Pour créer un objet thread en Python, vous pouvez utiliser le constructeur Thread
: threading.Thread(
...). Il s'agit de la syntaxe générique qui suffit pour la plupart des implémentations de threads :
threading.Thread(target=...,args=...)
Ici,
target
est le mot-clé qui désigne un appelable Pythonargs
est le tuple d'arguments que la cible prend en compte.
Vous aurez besoin de Python 3.x pour exécuter les exemples de code de ce tutoriel. Téléchargez le code et suivez-le.
Définir et exécuter des threads en Python
Définissons un thread qui exécute une fonction cible.
La fonction cible est some_func
.
import threading
import time
def some_func() :
print("Running some_func...")
time.sleep(2)
print("Finished running some_func.")
thread1 = threading.Thread(target=some_func)
thread1.start()
print(threading.active_count())
Analysons ce que fait l'extrait de code ci-dessus :
- Il importe les modules
threading
ettime
. - La fonction
some_func
comporte des instructionsprint()
descriptives et inclut une opération de mise en veille pendant deux secondes :time.sleep(n
) met la fonction en veille pendantn
seconde. - Ensuite, nous définissons un fil
thread_1
dont la cible estsome_func
.threading.Thread(target=...)
crée un objet thread. - Remarque: indiquez le nom de la fonction et non un appel de fonction ; utilisez
some_func
et nonsome_func()
. - La création d'un objet thread ne démarre pas un thread ; c'est l'appel de la méthode
start()
sur l'objet thread qui le fait. - Pour obtenir le nombre de threads actifs, nous utilisons la fonction
active_count()
.
Le script Python s'exécute sur le thread principal et nous créons un autre thread(thread1
) pour exécuter la fonction some_func
; le nombre de threads actifs est donc de deux, comme le montre la sortie :
# Output
Running some_func...
2
Finished running some_func.
Si nous examinons la sortie de plus près, nous constatons qu'au démarrage du thread1
, la première instruction d'impression s'exécute. Mais pendant l'opération de mise en veille, le processeur passe au thread principal et imprime le nombre de threads actifs, sans attendre la fin de l'exécution du thread1
.

Attente de la fin de l'exécution des threads
Si vous souhaitez que le fil 1
termine l'exécution, vous pouvez appeler la méthode join()
après avoir démarré le thread. Vous attendrez ainsi que le thread 1
termine son exécution sans passer au thread principal.
import threading
import time
def some_func() :
print("Running some_func...")
time.sleep(2)
print("Finished running some_func.")
thread1 = threading.Thread(target=some_func)
thread1.start()
thread1.join()
print(threading.active_count())
Maintenant, le thread1
a fini de s'exécuter avant que nous n'affichions le nombre de threads actifs. Ainsi, seul le thread principal est en cours d'exécution, ce qui signifie que le nombre de threads actifs est de un. ✅
# Output
Running some_func...
Finished running some_func.
1
Comment exécuter plusieurs threads en Python
Ensuite, créez deux fils pour exécuter deux fonctions différentes.
Ici, count_down
est une fonction qui prend un nombre en argument et compte à rebours à partir de ce nombre jusqu'à zéro.
def count_down(n) :
for i in range(n,-1,-1) :
print(i)
Nous définissons count_up
, une autre fonction Python qui compte à partir de zéro jusqu'à un nombre donné.
def count_up(n) :
for i in range(n 1) :
print(i)
lorsque vous utilisez la fonction
range()
avec la syntaxerange(start, stop, step)
, le point finalstop
est exclu par défaut.- Pour compter à rebours à partir d'un nombre spécifique jusqu'à zéro, vous pouvez utiliser une valeur de
pas
négative de -1 et définir la valeur destop
à -1 de manière à ce que zéro soit inclus.- De même, pour compter jusqu'à
n
, vous devez fixer la valeur d'arrêt
àn 1.
Les valeurs par défaut destart
etstep
étant respectivement 0 et 1, vous pouvez utiliserrange(n
1) pour obtenir la séquence de 0 à n.
Ensuite, nous définissons deux fils, thread1
et thread2
, pour exécuter les fonctions count_down
et count_up
, respectivement. Nous ajoutons des instructions d'impression
et des opérations de mise en veille
pour les deux fonctions.
Lors de la création des objets thread, notez que les arguments de la fonction cible doivent être spécifiés sous la forme d'un tuple dans le paramètre args
. Comme les deux fonctions(count_down
et count_up
) ne prennent qu'un seul argument, vous devrez insérer une virgule explicitement après la valeur. Cela permet de s'assurer que l'argument est toujours transmis sous la forme d'un tuple, car les éléments suivants sont déduits comme étant None
.
import threading
import time
def count_down(n) :
for i in range(n,-1,-1) :
print("Running thread1....")
print(i)
time.sleep(1)
def count_up(n) :
for i in range(n 1) :
print("Running thread2...")
print(i)
time.sleep(1)
thread1 = threading.Thread(target=count_down,args=(10,))
thread2 = threading.Thread(target=count_up,args=(5,))
thread1.start()
thread2.start()
Dans la sortie :
- La fonction
count_up
s'exécute sur lethread2
et compte jusqu'à 5 en partant de 0. - La fonction
count_down
s'exécute sur lethread1
et compte de 10 à 0.
# Sortie
Exécution du fil1....
10
Exécution du fil2...
0
Exécution du fil1....
9
Exécution du fil2...
1
Exécution du fil1....
8
Exécution du fil2...
2
Exécution du fil1....
7
Exécution du fil2...
3
Fil d'exécution1....
6
Fil d'exécution2...
4
Fil d'exécution1....
5
Fil d'exécution2...
5
Fil d'exécution1....
4
Fil d'exécution1....
3
Fil d'exécution1....
2
Fil d'exécution1....
1
Fil d'exécution1....
0
Vous pouvez constater que les threads 1
et 2
s'exécutent alternativement, car ils impliquent tous deux une opération d'attente (sleep). Une fois que la fonction count_up
a fini de compter jusqu'à 5, le thread2
n'est plus actif. Nous obtenons donc la sortie correspondant au seul thread1
.
Récapitulation
Dans ce tutoriel, vous avez appris à utiliser le module de threading intégré à Python pour mettre en œuvre le multithreading. Voici un résumé des principaux points à retenir :
- Le constructeur Thread peut être utilisé pour créer un objet thread. L'utilisation de threading.Thread(target=,args=()) crée un thread qui exécute le callable cible avec les arguments spécifiés dans args.
- Le programme Python s'exécute sur un thread principal, de sorte que les objets threads que vous créez sont des threads supplémentaires. Vous pouvez appeler la fonction active_count() qui renvoie le nombre de threads actifs à tout moment.
- Vous pouvez démarrer un thread à l'aide de la méthode start() sur l'objet thread et attendre la fin de son exécution à l'aide de la méthode join().
Vous pouvez coder d'autres exemples en modifiant les temps d'attente, en essayant une opération d'E/S différente, etc. Veillez à mettre en œuvre le multithreading dans vos prochains projets Python. Bon codage!🎉