Oui, j'avais arrêté de publier ici, mais d'un point de vue marketing il vaut mieux continuer à publier... On continue.
Texte initialement publié ici.
L'objectif de ce texte est de donner un résumé direct des concepts de base nécessaires pour comprendre la concurrence et le parallélisme dans le langage Python. Je recommande d'avoir un minimum de connaissances sur le sujet ou de combiner ce texte avec des études provenant d'autres sources. Toutes les références sont en fin de texte.
Je couvrirai les sujets suivants :
En informatique, un processus est une instance d'une application en cours d'exécution. Si vous ouvrez une application sur votre ordinateur, telle qu'un navigateur, cette application sera associée à un processus. Un processus est composé de :
L'image suivante est tirée du livre de Francis Machado et Luis Maia :
Ces informations sont nécessaires pour exécuter un programme.
Un thread est un sous-programme d'un programme, étant la plus petite unité d'exécution gérée par un système d'exploitation et un composant d'un processus.
Les différents threads d'un processus hypothétique peuvent être exécutés simultanément (ce que nous comprendrons bientôt), partageant des ressources telles que la mémoire. Différents processus ne partagent pas ces ressources.
L'image ci-dessous est tirée de Wikipédia :
En interprétant l'image ci-dessus, nous pouvons extraire qu'un programme est enregistré sur disque (mémoire secondaire non volatile) et comprend plusieurs instructions, et peut être instancié (démarré) dans un ou plusieurs processus, et ceux-ci peuvent à leur tour avoir plusieurs fils de discussion associés.
Ces deux expressions apparaissent beaucoup dans la discussion sur la concurrence et peuvent apparaître en portugais avec I/O (entrée/sortie) et CPU (unité centrale de traitement).
Lorsque nous parlons de limites d'E/S et de CPU, nous parlons des facteurs limitants qui empêchent une opération de s'exécuter plus rapidement sur notre ordinateur, et nous pouvons trouver ces deux types d'opérations dans la même base de code.
Une opération liée au processeur est gourmande en ressources processeur et s'exécutera plus rapidement si le processeur est plus puissant. En d’autres termes, si nous passons de 2 GHz à 4 GHz, cette opération sera probablement plus rapide. On parle ici d'opérations qui effectuent de nombreux calculs, calculs ; par exemple, comment calculer Pi.
Une opération liée aux E/S dépend de la vitesse du réseau et de la vitesse des périphériques d'entrée et de sortie. Faire une requête à un serveur Web ou lire un fichier à partir du disque sont des opérations liées aux E/S.
Les deux types d'opérations peuvent bénéficier de l'utilisation de la concurrence.
GIL signifie verrouillage global de l'interpréteur, qui vise à empêcher un processus Python d'exécuter plus d'un bytecode d'instruction Python en même temps. Pour exécuter un thread, il est nécessaire "d'acquérir" le GIL et pendant qu'un thread détient le GIL, un autre thread ne peut pas l'acquérir en même temps. Cela ne veut pas dire que nous ne pouvons pas avoir plus d'un fil de discussion dans ce contexte.
Nous envisageons ici l'implémentation de référence Python. CPython est l'implémentation standard de Python, utilisée comme référence pour le comportement du langage. Il existe d'autres implémentations, telles que Jython ou IronPython. GIL est présent dans CPython et ce n'est que récemment que nous avons eu un PEP (Python Enhancement Proposal) proposant de rendre GIL facultatif.
L'idée de GIL est d'éviter les conditions de concurrence, qui peuvent survenir lorsque plusieurs threads doivent référencer un objet Python en même temps. Si plusieurs threads modifient une variable partagée, cette variable peut se trouver dans un état inattendu. Image tirée du livre de Matthew Fowler :
Dans l'image ci-dessus, deux threads tentent d'augmenter un nombre de références simultanément, et au lieu que le nombre donne 2, puisque les deux augmentent 1, le résultat final donne 1 (chaque thread est une colonne).
La concurrence en informatique se produit lorsque s'occupe de plus d'une tâche, sans nécessairement exécuter ces deux tâches exactement en même temps. Une phrase bien connue de Rob Pyke sur le sujet :
La compétition signifie faire face à plusieurs choses en même temps. Le parallélisme consiste à faire plusieurs choses en même temps.
Pensez à cette situation hypothétique : si vous comptez réaliser deux gâteaux, vous pouvez commencer par préchauffer le four et, en attendant, préparer la pâte du premier gâteau. Une fois le four à bonne température, vous pouvez placer la pâte du premier gâteau au four et, en attendant que le gâteau lève au four, vous pouvez préparer la pâte du deuxième gâteau. L'idée de la compétition est fondamentalement la suivante, vous n'avez pas besoin d'être inactif, bloqué, arrêté, en attendant qu'une tâche soit terminée, vous pouvez faire un changer et changer de tâche.
Dans ce contexte nous avons deux types de multitâches :
L'image ci-dessous permet de résumer la concurrence en Python :
Le parallélisme signifie que plusieurs tâches sont exécutées en même temps. En d’autres termes, le parallélisme implique la concurrence (traiter plusieurs tâches), mais la concurrence n’implique pas le parallélisme (les tâches ne sont pas nécessairement exécutées en parallèle au même moment). Pour que le parallélisme soit possible, nous avons besoin de plus d'un cœur de processeur.
En Python, le parallélisme est obtenu, par exemple, avec la bibliothèque multitraitement, où nous aurons plus d'un processus Python, chacun avec son propre GIL. L'image permet d'illustrer le parallélisme en Python :
Il existe différentes manières d'obtenir la concurrence et le parallélisme en Python et nous pouvons utiliser certaines bibliothèques pour optimiser notre code, en fonction du type d'opération que nous traitons, liée aux E/S ou au CPU. asyncio est une lib pour obtenir la concurrence en utilisant async et wait. Extrait de la documentation :
Asyncio est utilisé comme base pour plusieurs frameworks Python asynchrones qui fournissent des serveurs réseau et Web hautes performances, des bibliothèques de connexion à des bases de données, des files d'attente de tâches distribuées, etc.
Comme vous pouvez l'imaginer, cette bibliothèque convient à l'optimisation des tâches liées aux E/S, où nous avons un temps d'attente réseau, d'écriture sur disque, etc. Dans une opération liée au CPU, il n'y a pas d'attente, nous dépendons uniquement de la vitesse de calcul du CPU.
La bibliothèque de threads de Python nous permet d'exploiter plus d'un thread, cependant, nous avons toujours affaire à un cœur de processeur et à un processus Python, et rappelez-vous qu'il s'agit d'un cas de multitâche préemptif où le système d'exploitation le fait la tâche change pour nous. La bibliothèque est également plus utile pour optimiser les opérations liées aux E/S.
À propos du threading, le site Real Python fournit quelques points importants :
Étant donné que le système d'exploitation contrôle le moment où une tâche s'arrêtera et une autre tâche démarrera, toutes les données partagées entre les threads doivent être protégées, ou thread-safe. Malheureusement, request.Session() n'est pas thread-safe. Il existe plusieurs stratégies pour rendre l'accès aux données thread-safe en fonction de la nature des données et de la manière dont vous les utilisez. L'un d'eux consiste à utiliser des structures de données thread-safe comme file d'attente du module de file d'attente Python.
Nous avons trouvé la documentation sur la file d'attente ici.
À propos de la bibliothèque multitraitement dans la documentation Python :
multiprocessing est un package qui prend en charge la génération de processus à l'aide d'une API similaire au module de threading. Le package multitraitement fournit une concurrence locale et distante, contournant efficacement le GIL en utilisant des sous-processus au lieu de threads. C'est pourquoi le module multitraitement permet au programmeur de profiter de plusieurs processeurs sur une seule machine.
Il convient de souligner que l'exécution de plus d'un processus sur différents cœurs de processeur ne signifie pas désactiver le GIL, mais plutôt que chaque processus aura son propre GIL. En tirant parti de plusieurs cœurs de processeur et en partageant de lourdes charges de travail de processeur entre plusieurs cœurs disponibles, la bibliothèque est plus adaptée aux contraintes de processeur.
Sources :
FOWLER, Matthieu. Concurrence Python avec asyncio. Publications Manning, 2022.
MACHADO, Francis Bérenger ; MAIA, Luiz Paulo. Architecture des systèmes d'exploitation : y compris des exercices avec le simulateur SOSIM et des questions ENADE. Rio de Janeiro : LTC, 2013.
Thread (informatique) par Wikipédia
Accélérez votre programme Python avec la concurrence par Real Python
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!