Accéder au contenu principal

Guide ultime : tutoriel sur le threading en Python

Découvrez les concepts de base du threading et comment les utiliser en Python.
Actualisé 19 sept. 2026  · 12 min lire

Explorer avec l’IA

ChatGPTClaudePerplexity

Si vous débutez en Python et souhaitez aller plus loin, suivez le cours Introduction to Data Science in Python de DataCamp.

\n
\"graphic\"
\n

Introduction

\n

Le threading permet d'exécuter en parallèle différentes parties d'un même processus (Source : RealPython). Ces différentes parties sont généralement indépendantes et correspondent à des unités d'exécution distinctes appartenant au même processus. Un processus n'est rien d'autre qu'un programme en cours d'exécution, composé d'unités pouvant s'exécuter concurremment. Par exemple : un navigateur web est un processus, une application qui pilote plusieurs caméras en même temps en est un autre ; un jeu vidéo en est également un.

\n

Au sein d'un processus, on parle de multi-threading lorsque plusieurs threads collaborent pour atteindre un même objectif. L'intérêt principal des threads est de permettre une exécution parallèle du programme.

\n

Illustrons le concept avec un exemple. Imaginez une application qui compte les voitures entrant et sortant du parking d'un centre commercial. Votre dispositif relie plusieurs caméras à un boîtier central pour surveiller entrées et sorties. Chaque caméra exécute un algorithme de comptage au sein du même processus. Cependant, chaque caméra et l'algorithme qui la traite peuvent tourner dans un thread distinct. De même, la lecture des images de la caméra et la prédiction de l'algorithme sur ces images peuvent être gérées par deux threads séparés.

\n

Autre exemple : dans un jeu vidéo, le processus doit traiter en parallèle les graphismes, les interactions utilisateur et le réseau (en mode multijoueur) pour rester réactif en permanence. Pour cela, il s'appuie sur le multi-threading, chaque thread prenant en charge une tâche indépendante.

\n

Un thread possède son propre flot d'exécution, ce qui signifie que plusieurs opérations se déroulent simultanément au sein du processus.

\n

Important : chaque processus dispose au minimum d'un thread, appelé main thread. Si votre programme ne crée aucun thread, il fonctionne au moins sur ce thread principal, c‑est‑à‑dire le processus lui-même.

\n

En observant le schéma ci‑dessus, on voit un processus composé du thread principal contenant des variables globales, et de trois threads différents t1, t2 et t3. Chaque thread a ses variables locales et son propre flux d'exécution. Les threads partagent généralement les ressources du processus : les ressources accessibles au thread principal sont également partagées avec t1, t2 et t3. Tous les threads accèdent aux variables globales tout en conservant leurs variables locales.

\n

Il existe différents types de threads :

\n
    \n
  • Thread noyau (kernel thread)
  • \n
  • Thread utilisateur
  • \n
  • Combinaison de thread noyau et utilisateur
  • \n
\n

Voyons maintenant quelques avantages à utiliser des threads dans votre programme :

\n
    \n
  • \n

    Le multi-threading peut accélérer l'exécution si plusieurs cœurs CPU sont disponibles.

    \n
  • \n
  • \n

    Il permet aussi d'effectuer d'autres tâches pendant les opérations d'E/S grâce à plusieurs threads, voire au thread principal associé à un thread unique. Par exemple, la vitesse de lecture des images par la caméra et celle de l'inférence de l'algorithme peuvent être gérées par des threads différents : l'algorithme n'attend pas la prochaine image, et la lecture des images n'attend pas la fin de l'inférence pour continuer.

    \n
  • \n
\n
    \n
  • Les threads d'un même processus peuvent partager la mémoire et les ressources du thread principal.
  • \n
\n

Les défis du threading

\n
    \n
  • \n

    N'oubliez pas que Python s'appuie sur l'implémentation CPython, qui limite l'exécution à un seul thread à la fois ; le threading ne va donc pas accélérer toutes les tâches. La raison principale est le Global Interpreter Lock (GIL).

    \n

    Pour en savoir plus sur le GIL, consultez ce tutoriel.

    \n
  • \n
  • \n

    Si vous cherchez à accélérer une tâche très consommatrice en CPU, le threading n'est sans doute pas la meilleure option. Dans ce cas, le multi-processing est souvent plus adapté.

    \n
  • \n
  • \n

    Pour exécuter un programme composé de nombreux threads, il faut pouvoir basculer rapidement de l'un à l'autre ; la planification (scheduling) doit donc être soignée dans les programmes très multi-threadés.

    \n
  • \n
  • \n

    Le partage des ressources peut poser problème : tous les threads accèdent aux mêmes ressources et à la même mémoire globale. Une opération dans un thread peut provoquer une erreur mémoire dans un autre, ou l'empêcher d'obtenir la mémoire nécessaire à son exécution.

    \n
  • \n
\n

Le threading en Python

\n
    \n
  • \n

    En Python, le module intégré pour le threading s'appelle threading et peut être importé directement.

    \n
  • \n
  • \n

    Comme presque tout en Python est un objet, un thread est lui aussi un objet. Un thread peut :

    \n
      \n
    • Conserver des données,
    • \n
    • Être stocké dans des structures comme des dictionnaires, listes, ensembles, etc.
    • \n
    • Être passé en paramètre à une fonction.

    • \n
    \n
  • \n
  • \n

    Un thread peut aussi être exécuté comme un processus.

    \n
  • \n
  • \n

    Un thread en Python peut adopter différents états :

    \n
      \n
    • Attente (wait),
    • \n
    • Verrouillé (locked).
    • \n
    \n
  • \n
\n

Voyons maintenant comment implémenter le threading en Python.

\n

Module thread en Python 3

\n

Notez que Python 3 reste compatible avec le module thread présent en Python 2.7. En Python 3, il s'importe sous le nom de module _thread. Prenons un exemple pour comprendre _thread.

\n
import _thread #thread module imported\nimport time #time module\n
\n

Définissons une fonction thread_delay qui prend deux paramètres en entrée : le nom du thread et le délai. Dans cette fonction, vous allez :

\n
    \n
  • Initialiser un compteur à zéro,
  • \n
  • Boucler avec une boucle while exécutée trois fois,
  • \n
  • Dans la boucle, utiliser time.sleep() pour ajouter un délai, utile pour visualiser l'exécution du thread (en secondes),
  • \n
  • Incrémenter le compteur de 1,
  • \n
  • Afficher le nom du thread et l'horodatage pour suivre son exécution.
  • \n
\n
def thread_delay(thread_name, delay):\n    count = 0\n    while count < 3:\n        time.sleep(delay)\n        count += 1\n        print(thread_name, '-------->', time.time())\n
\n

Pour exécuter la fonction ci‑dessus dans un thread, utilisez la méthode start_new_thread du module _thread.

\n

Regardons la docstring de la méthode start_new_thread.

\n
?_thread.start_new_thread\n
\n
\"Threading
\n

Passons la fonction thread_delay sans parenthèses ainsi que deux arguments : le nom du thread et le délai (pour visualiser l'exécution, très rapide sinon).

\n
_thread.start_new_thread(thread_delay, ('t1', 1))\n_thread.start_new_thread(thread_delay, ('t2', 3))\n
\n
10916\n\n\n\nt1 --------> 1588077856.4221454\nt1 --------> 1588077857.4418247\nt2 --------> 1588077858.422453\nt1 --------> 1588077858.4431\nt2 --------> 1588077861.4371448\nt2 --------> 1588077864.438915\n
\n

On constate que le thread t1 commence à s'exécuter en premier. Pendant ce temps, t2 attend en raison d'un délai de 3 secondes ; dès que ce délai se termine, t2 s'exécute, alors que le délai de t1 n'est que d'une seconde.

\n

Modifions maintenant le délai de t2 à 5 secondes, ce qui fera démarrer t2 après la fin de t1, puisque t1 met seulement 3 secondes à s'exécuter.

\n
_thread.start_new_thread(thread_delay, ('t1', 1))\n_thread.start_new_thread(thread_delay, ('t2', 5))\n
\n
14632\n\n\n\nt1 --------> 1588078001.4087512\nt1 --------> 1588078002.4272866\nt1 --------> 1588078003.432218\nt2 --------> 1588078005.4082909\nt2 --------> 1588078010.4268317\nt2 --------> 1588078015.4283602\n
\n

Implémenter le threading avec le module Threading

\n

Reprenons le même exemple, mais en utilisant cette fois le module threading au lieu de _thread.

\n
import threading\nimport time\n
\n
def thread_delay(thread_name, delay):\n    count = 0\n    while count < 3:\n        time.sleep(delay)\n        count += 1\n        print(thread_name, '-------->', time.time())\n
\n

Le module threading propose une classe Thread, dans le même esprit que la fonction start_new_thread du module _thread.

\n

Affichons la docstring de la classe Thread, qui accepte plusieurs paramètres comme group, target (une fonction), args, etc.

\n
?threading.Thread\n
\n
\"Threading
\n

Dans le constructeur de Thread, passez la fonction cible thread_delay et ses arguments.

\n
t1 = threading.Thread(target=thread_delay, args=('t1', 1))\nt2 = threading.Thread(target=thread_delay, args=('t2', 3))\n
\n

Avec le module threading, pour lancer un thread, utilisez la méthode start(), chargée de démarrer son exécution.

\n
?t1.start\n
\n
\"Threading
\n
t1.start()\nt2.start()\n
\n
t1 --------> 1588081121.7790291\nt1 --------> 1588081122.7962046\nt2 --------> 1588081123.7789369\nt1 --------> 1588081123.7980876\nt2 --------> 1588081126.7961557\nt2 --------> 1588081129.7995002\n
\n

Vous utiliserez aussi la méthode join, qui attend la fin de tous les threads. Ainsi, le code situé après les join ne s'exécutera qu'une fois les threads terminés.

\n
?t1.join\n
\n
\"Threading
\n
t1.start()\nt2.start()\n\nt1.join()\nt2.join()\n\nprint(\"Thread execution is complete!\")\n
\n
t1 --------> 1588081798.7118263\nt1 --------> 1588081799.7231226\nt2 --------> 1588081800.7129223\nt1 --------> 1588081800.730732\nt2 --------> 1588081803.7298765\nt2 --------> 1588081806.7309563\nThread execution is complete!\n
\n

Calculons maintenant le volume d'un cube et d'un carré en utilisant le threading : c'est très simple.

\n
def volume_cube(a):\n    print (\"Volume of Cube:\", a*a*a)\n
\n
def volume_square(a):\n    print (\"Volume of Square:\", a*a)\n
\n
t1 = threading.Thread(target=volume_cube, args=(2))\nt2 = threading.Thread(target=volume_square, args=(3))\n
\n
t1.start()\nt2.start()\n\nt1.join()\nt2.join()\n\nprint(\"Thread execution is complete!\")\n
\n
Thread execution is complete!\n\n\nException in thread Thread-9:\nTraceback (most recent call last):\n  File \"C:\\Program Files\\Anaconda3\\lib\\threading.py\", line 926, in _bootstrap_inner\n    self.run()\n  File \"C:\\Program Files\\Anaconda3\\lib\\threading.py\", line 870, in run\n    self._target(*self._args, **self._kwargs)\nTypeError: volume_square() argument after * must be an iterable, not int\nException in thread Thread-8:\nTraceback (most recent call last):\n  File \"C:\\Program Files\\Anaconda3\\lib\\threading.py\", line 926, in _bootstrap_inner\n    self.run()\n  File \"C:\\Program Files\\Anaconda3\\lib\\threading.py\", line 870, in run\n    self._target(*self._args, **self._kwargs)\nTypeError: volume_cube() argument after * must be an iterable, not int\n
\n

Comment ça ? Dès l'appel à start, une erreur survient. C'est parce que le paramètre args attend un tuple. Or volume_cube et volume_square n'ont qu'un seul paramètre : il faut donc ajouter une virgule dans args après la première valeur.

\n
t1 = threading.Thread(target=volume_cube, args=(2,))\nt2 = threading.Thread(target=volume_square, args=(3,))\n
\n
t1.start()\nt2.start()\n\nt1.join()\nt2.join()\n\nprint(\"Thread execution is complete!\")\n
\n
Volume of Cube: 8\nVolume of Square: 9\nThread execution is complete!\n
\n

Voyons maintenant comment utiliser le threading via une sous-classe.

\n

Threading comme sous-classe

\n

Dans cette section, vous allez créer une sous-classe de la classe Thread fournie par le module threading, puis instancier des threads t1 et t2 à partir de celle‑ci.

\n

Nous allons réutiliser la fonction thread_delay.

\n
class DataCampThread(threading.Thread):\n    def __init__(self, name, delay):\n        threading.Thread.__init__(self)\n        self.name = name\n        self.delay = delay\n\n\n    def run(self):\n        print('Starting Thread:', self.name)\n        thread_delay(self.name,self.delay)\n        print('Execution of Thread:', self.name, 'is complete!')\n\n\n t1 = DataCampThread('t1', 1)\n t2 = DataCampThread('t2', 3)    \n\n t1.start()\n t2.start()\n\n t1.join()\n t2.join()\n\n print(\"Thread execution is complete!\")\n
\n
Starting Thread: t1\nStarting Thread: t2\nt1 --------> 1588089083.4879806\nt1 --------> 1588089084.489222\nt1 --------> 1588089085.4909666\nExecution of Thread: t1 is complete!\nt2 --------> 1588089085.4979634\nt2 --------> 1588089088.499365\nt2 --------> 1588089091.5012596\nExecution of Thread: t2 is complete!\nThread execution is complete!\n
\n

Explications pas à pas :

\n
    \n
  • \n

    Vous définissez une classe DataCampThread dont l'argument de classe parent est threading.Thread. L'objectif est de créer une sous‑classe du module threading. Vous bénéficiez ainsi de toutes les méthodes de threading.Thread, comme avec l'héritage.

    \n
  • \n
  • \n

    Vous définissez ensuite la méthode __init__ de DataCampThread et y passez le nom et le délai. C'est le constructeur, analogue aux paramètres transmis à threading.Thread, mais ici à la classe DataCampThread.

    \n
  • \n
  • \n

    Vous initialisez le __init__ de la classe mère threading.Thread, puis affectez name et delay.

    \n
  • \n
  • \n

    Vous définissez la méthode run. Rappelez‑vous ici l'appel à start : sous le capot, il invoque run. Puisque vous héritez de la classe mère, vous pouvez surcharger run. Ainsi, lors de l'appel à start, vos modifications dans run seront prises en compte.

    \n
  • \n
\n

Conclusion

\n

Félicitations pour avoir terminé ce tutoriel.

\n

Ce tutoriel proposait une introduction aux bases du threading en Python. Le sujet est vaste et essentiel ; certains aspects n'ont volontairement pas été abordés. N'hésitez pas à explorer d'autres méthodes comme active_count, current_thread, etc.

\n

Pour aller plus loin, consultez aussi la documentation Python sur le parallélisme à base de threads.

\n

Si vous débutez en Python et souhaitez approfondir, suivez le cours Introduction to Data Science in Python de DataCamp.

Sujets
Python

Cours Python

Cours

Introduction à Python

4 h
7M
Apprenez les bases de l’analyse de données avec Python en quatre heures et explorez ses principaux packages.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow