Geekflare est soutenu par son public. Nous pouvons percevoir des commissions d'affiliation sur les liens d'achat présents sur ce site.
En Développement Dernière mise à jour : 25 septembre 2023
Partager sur :
Invicti Web Application Security Scanner - la seule solution qui offre une vérification automatique des vulnérabilités avec Proof-Based Scanning™.

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.

cpu-proc-threads

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.

processus et threads

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éristiqueProcessusFil de discussion
MémoireMémoire dédiéeMémoire partagée
Mode d'exécutionParallèle, concurrentConcurrent, mais pas parallèle
Exécution gérée par Le système d'exploitationInterprè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.

multicore-parallélisme

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.

code

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 Python
  • args 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 et time.
  • La fonction some_func comporte des instructions print() descriptives et inclut une opération de mise en veille pendant deux secondes : time.sleep(n ) met la fonction en veille pendant n seconde.
  • Ensuite, nous définissons un fil thread_1 dont la cible est some_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 non some_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.

fil1-ex

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 syntaxe range(start, stop, step), le point final stop 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 de stop à -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 de start et step étant respectivement 0 et 1, vous pouvez utiliser range(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 le thread2 et compte jusqu'à 5 en partant de 0.
  • La fonction count_down s'exécute sur le thread1 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!🎉

  • Bala Priya C
    Auteur
Merci à nos sponsors
D'autres lectures intéressantes sur le développement
Alimentez votre entreprise
Quelques outils et services pour aider votre entreprise à se développer.
  • Invicti utilise le Proof-Based Scanning™ pour vérifier automatiquement les vulnérabilités identifiées et générer des résultats exploitables en quelques heures seulement.
    Essayez Invicti
  • Web scraping, proxy résidentiel, proxy manager, web unlocker, search engine crawler, et tout ce dont vous avez besoin pour collecter des données web.
    Essayez Brightdata
  • Monday.com est un système d'exploitation tout-en-un qui vous aide à gérer vos projets, vos tâches, votre travail, vos ventes, votre CRM, vos opérations, vos flux de travail et bien plus encore.
    Essayez le lundi
  • Intruder est un scanner de vulnérabilité en ligne qui détecte les faiblesses de votre infrastructure en matière de cybersécurité, afin d'éviter des violations de données coûteuses.
    Essayer l'intrus