Comprendre l'asyncio de Python | Journal Linux – Bien choisir son serveur d impression
Author: Titanfall —
Short summary: Comment commencer à utiliser asyncio de Python. Plus tôt cette année, j'ai assisté à PyCon, le salon international Python conférence. Un sujet, présenté lors de nombreuses conférences et discuté officieusement dans le couloir, était l'état de filetage en Python – qui n'est, en un mot, ni idéal ni aussi terrible que certains critiques se disputer. […]
Quick overview
- Site
- Tutos GameServer
- Canonical URL
- https://tutos-gameserver.fr/2020/03/11/comprendre-lasyncio-de-python-journal-linux-bien-choisir-son-serveur-d-impression/
- LLM HTML version
- https://tutos-gameserver.fr/2020/03/11/comprendre-lasyncio-de-python-journal-linux-bien-choisir-son-serveur-d-impression/llm
- LLM JSON version
- https://tutos-gameserver.fr/2020/03/11/comprendre-lasyncio-de-python-journal-linux-bien-choisir-son-serveur-d-impression/llm.json
- Manifest
- https://tutos-gameserver.fr/llm-endpoints-manifest.json
- Estimated reading time
- 12 minutes (673 seconds)
- Word count
- 2241
Key points
- Comment commencer à utiliser asyncio de Python.
- Plus tôt cette année, j'ai assisté à PyCon, le salon international Python conférence.
- Un sujet, présenté lors de nombreuses conférences et discuté officieusement dans le couloir, était l'état de filetage en Python – qui n'est, en un mot, ni idéal ni aussi terrible que certains critiques se disputer.
- Un sujet connexe qui a été soulevé à plusieurs reprises était celui de "l'asyncio", un approche relativement nouvelle de la concurrence en Python.
Structured content
Comment commencer à utiliser asyncio de Python. Plus tôt cette année, j'ai assisté à PyCon, le salon international Python conférence. Un sujet, présenté lors de nombreuses conférences et discuté officieusement dans le couloir, était l'état de filetage en Python – qui n'est, en un mot, ni idéal ni aussi terrible que certains critiques se disputer.
Un sujet connexe qui a été soulevé à plusieurs reprises était celui de "l'asyncio", un approche relativement nouvelle de la concurrence en Python. Non seulement il y avait des présentations formelles et des discussions informelles sur l'asyncio, mais nombre de personnes m'ont également posé des questions sur les cours sur le sujet.
Je dois admettre que j'ai été un peu surpris par tout l'intérêt. Après tous, asyncio n'est pas un nouvel ajout à Python; ça fait un moment quelques années. Et, cela ne résout pas tous les problèmes associés à fils. De plus, il peut être déroutant pour de nombreuses personnes de commencer avec cela.
Et pourtant, on ne peut nier qu'après un certain nombre d'années où les gens ont ignoré l'asyncio, ça commence à prendre de l'ampleur. Je suis sûr en partie parce que l'asyncio a mûri et s'est amélioré au fil du temps, merci en grande partie au travail dévoué de nombreux développeurs. Mais c'est aussi parce que l'asyncio est un choix de plus en plus bon et utile pour certains types de tâches, en particulier les tâches qui les réseaux.
Donc, avec cet article, je lance une série sur asyncio – ce que c'est, comment l'utiliser, où cela est approprié, et comment vous pouvez et devez (et ne pouvez pas non plus et ne devrait pas) l'intégrer dans votre propre travail.
Qu'est-ce que l'asyncio? Tout le monde est habitué à ce que les ordinateurs soient capables de faire plus d'une chose à la fois. le temps – enfin, en quelque sorte. Bien qu’il puisse sembler que les ordinateurs sont faire plus d'une chose à la fois, ils sont en train de changer, très rapidement, à travers différentes tâches. Par exemple, lorsque vous ssh dans un Linux serveur, il peut sembler qu'il n'exécute que vos commandes. Mais en réalité, vous obtenez une petite "tranche de temps" du CPU, avec le reste aller à d'autres tâches sur l'ordinateur, tels que les systèmes qui gérer la mise en réseau, la sécurité et divers protocoles. En effet, si vous êtes en utilisant SSH pour se connecter à un tel serveur, certaines de ces tranches de temps sont utilisés par sshd pour gérer votre connexion et même vous permettre de émettre des commandes.
Tout cela se fait, sur les systèmes d'exploitation modernes, via "préemption multitâche ". En d'autres termes, les programmes en cours d'exécution n'ont pas le choix quand ils abandonneront le contrôle du CPU. Ils sont plutôt obligés de abandonnez le contrôle et reprenez un peu plus tard. Chaque processus exécuté sur un ordinateur est géré de cette façon. Chaque processus peut, à son tour, utiliser des threads, des sous-processus qui subdivisent la tranche de temps donnée à leur processus parent.
Donc, sur un ordinateur hypothétique avec cinq processus (et un cœur), chacun processus obtiendrait environ 20% du temps. Si l'un de ces processus était pour avoir quatre threads, chaque thread obtiendrait 5% du temps du processeur. (Les choses sont évidemment plus complexes que cela, mais c'est un bon moyen de pensez-y à un niveau élevé.)
Python fonctionne très bien avec les processus via le "multiprocessing" bibliothèque. Le problème avec les processus est qu'ils sont relativement grands et encombrants, et vous ne pouvez pas les utiliser pour certaines tâches, telles que l'exécution d'un fonction en réponse à un clic de bouton, tout en gardant l'interface utilisateur réactive.
Donc, vous voudrez peut-être utiliser des threads. Et en effet, les threads de Python fonctionnent, et ils fonctionnent bien, pour de nombreuses tâches. Mais ils ne sont pas aussi bons qu'ils pourraient l'être, à cause du GIL (le verrou d'interpréteur global), qui garantit que un seul thread s'exécute à la fois. Alors bien sûr, Python vous laissera courir programmes multithread, et ceux-là même fonctionnent bien quand ils sont faire beaucoup d'E / S. C'est parce que les E / S sont lentes par rapport au CPU et mémoire, et Python peut en profiter pour desservir d'autres threads. Si vous utilisez des threads pour effectuer des calculs sérieux, Les threads de Python sont une mauvaise idée, et ils ne vous mèneront nulle part. Même avec de nombreux cœurs, un seul thread s'exécutera à la fois, ce qui signifie que vous êtes rien de mieux que d'exécuter vos calculs en série.
Les ajouts asyncio à Python offrent un modèle différent pour la concurrence. Comme pour les threads, asyncio n'est pas une bonne solution aux problèmes liés au processeur (c'est-à-dire, qui ont besoin de beaucoup de temps CPU pour effectuer les calculs). Cela ne convient pas non plus lorsque vous devez absolument que les choses fonctionnent vraiment en parallèle, comme cela arrive avec les processus.
Mais si vos programmes fonctionnent avec le réseau, ou s'ils font des E / S étendues, asyncio pourrait bien être une bonne façon de procéder.
La bonne nouvelle est que si cela est approprié, asyncio peut être beaucoup plus facile à travailler avec des fils.
La mauvaise nouvelle est que vous devrez penser d'une manière nouvelle et différente au travail avec asyncio.
Multitâche coopératif et coroutines Plus tôt, j'ai mentionné que les systèmes d'exploitation modernes utilisent multitâche "pour faire avancer les choses, forçant les processus à abandonner le contrôle du CPU en faveur d'un autre processus. Mais il y a un autre modèle, connu comme "multitâche coopératif", dans lequel le système attend qu'un programme abandonne volontairement le contrôle du CPU. D'où le mot «coopération» – si la fonction a décidé d'effectuer des tas de calculs, et jamais abandonne le contrôle, alors le système ne peut rien y faire.
Cela ressemble à une recette pour un désastre; pourquoi voudriez-vous écrire, encore moins exécuter, des programmes qui abandonnent le CPU? La réponse est simple. Quand ton programme utilise les E / S, vous pouvez à peu près garantir que vous serez attendre paresseusement jusqu'à ce que vous obteniez une réponse, compte tenu du ralentissement des E / S est que les programmes exécutés en mémoire. Ainsi, vous pouvez renoncer volontairement CPU chaque fois que vous faites quelque chose avec les E / S, sachant que assez tôt, d'autres De même, les programmes invoqueront les E / S et abandonneront le processeur, renvoyant contrôle pour vous.
Pour que cela fonctionne, vous aurez besoin de tous les programmes au sein de cet univers multitâche coopératif pour convenir d'un terrain règles. En particulier, vous en aurez besoin pour accepter que toutes les E / S vont à travers le système multitâche, et qu'aucune des tâches ne monopolisera le CPU pendant une longue période de temps.
Mais attendez, vous en aurez également besoin d'un peu plus. Vous devrez donner aux tâches un moyen de arrêtez de l'exécuter volontairement pendant un petit moment, puis redémarrez d'où ils se sont arrêtés.
Ce dernier morceau existe en fait en Python depuis un certain temps, mais avec syntaxe légèrement différente. Commençons le voyage et l'exploration de l'asyncio là-bas.
Une fonction Python normale, lorsqu'elle est appelée, s'exécute du début à la fin. Par exemple:
def foo (): impression ("a") impression ("b") impression ("c")
Si vous appelez cela, vous verrez:
une b c
Bien sûr, il est généralement bon pour les fonctions non seulement d'imprimer quelque chose, mais aussi pour retourner une valeur:
def bonjour (nom): retourner f'Bonjour, nom '
Maintenant, lorsque vous appelez la fonction, vous récupérez quelque chose. Vous pouvez saisir qui a renvoyé la valeur et l'assignez à une variable:
s = bonjour ('Reuven')
Mais il y a une variation sur revenir qui se révélera au cœur de ce vous faites ici, à savoir rendement. le rendement la déclaration ressemble et agit un peu comme revenir, mais il peut être utilisé plusieurs fois dans une fonction, même dans une boucle:
def bonjour (nom): pour i dans la plage (5): rendement f '[i] Bonjour, name '
Parce qu'il utilise rendement, plutôt que revenir, c'est ce qu'on appelle un "fonction générateur". Et quand vous l'invoquez, vous ne récupérez pas un chaîne, mais plutôt un Générateur objet:
>>> g = bonjour ('Reuven') >>> type (g) Générateur
UNE Générateur est une sorte d'objet qui sait se comporter à l'intérieur d'un Python pour boucle. (En d'autres termes, il implémente le protocole d'itération.)
Lorsqu'elle est placée dans une telle boucle, la fonction commence à s'exécuter. cependant, chaque fois que la fonction de générateur rencontre un rendement déclaration, il retourner la valeur à la boucle et se mettre en veille. Quand se réveille-t-il encore? Quand le pour la boucle demande que la prochaine valeur soit retournée l'itérateur:
pour s in g: impression (s)
Les fonctions de générateur fournissent ainsi l'essentiel de ce dont vous avez besoin: a fonction qui s'exécute normalement, jusqu'à ce qu'elle atteigne un certain point dans le code. À ce stade, il renvoie une valeur à son appelant et se met en veille. Quand le pour boucle demande la prochaine valeur au générateur, la fonction continue à s'exécuter là où il s'était arrêté (c'est-à-dire juste après la rendement comme si elle ne s'était jamais arrêtée.
Le fait est que les générateurs tels que décrits ici produisent une sortie, mais ne peuvent pas obtenir n'importe quelle entrée. Par exemple, vous pouvez créer un générateur pour en renvoyer un Nombre de Fibonacci par itération, mais vous ne pouvez pas lui dire de sauter dix chiffres à venir. Une fois que la fonction générateur est en cours d'exécution, elle ne peut pas entrées de l'appelant.
Il ne peut pas obtenir de telles entrées via le protocole d'itération normal, c'est-à-dire. Les générateurs prennent en charge un envoyer méthode, permettant au monde extérieur d'envoyer tout objet Python au générateur. De cette façon, les générateurs prennent désormais en charge communication bidirectionnelle. Par exemple:
def bonjour (nom): tandis que True: name = yield f'Bonjour, name ' sinon nom: Pause
Étant donné la fonction de générateur ci-dessus, vous pouvez maintenant dire:
>>> g = bonjour ('monde')
>>> suivant (g) 'Bonjour le monde'
>>> g.send ('Reuven') «Bonjour, Reuven»
>>> g.send ('Linux Journal') «Bonjour, Linux Journal»
En d'autres termes, vous exécutez d'abord la fonction de générateur pour obtenir un générateur objet ("g") en arrière. Vous devez ensuite l'amorcer avec le suivant une fonction, jusqu'à et y compris le premier rendement déclaration. À partir de ce pointez sur, vous pouvez soumettre toute valeur que vous voulez au générateur via le envoyer méthode. Jusqu'à ce que vous couriez g.send (Aucun), vous continuerez à recevoir sortie en arrière.
Utilisé de cette façon, le générateur est appelé "coroutine", c'est-à-dire qu'il a l'état et s'exécute. Mais, il s'exécute en tandem avec le principal routine, et vous pouvez l'interroger chaque fois que vous voulez en tirer quelque chose.
L'asyncio de Python utilise ces concepts de base, quoique légèrement syntaxe différente, pour atteindre ses objectifs. Et même si cela peut sembler une chose banale pour pouvoir envoyer des données dans des générateurs, et obtenir les choses reviennent régulièrement, c'est loin d'être le cas. En effet, cette fournit le cœur d'une infrastructure complète qui vous permet de créer des applications réseau efficaces pouvant gérer de nombreuses applications simultanées utilisateurs, sans la douleur des threads ou des processus.
Dans mon prochain article, je prévois de commencer à regarder la syntaxe spécifique d'Asyncio et comment elle correspond à ce que j'ai montré ici. Restez à l'écoute.
Click to rate this post! [Total: 0 Average: 0]
Topics and keywords
Themes: Serveur d'impression
License & attribution
License: CC BY-ND 4.0.
Attribution required: yes.
Manifest: https://tutos-gameserver.fr/llm-endpoints-manifest.json
LLM Endpoints plugin version 1.1.2.