Pour moi, c'est en fait assez simple:
L' option de sous-processus :
subprocess
est pour exécuter d'autres exécutables --- c'est fondamentalement un wrapper autour os.fork()
et os.execve()
avec un certain support pour la plomberie facultative (configuration des PIPE vers et depuis les sous-processus. Évidemment, vous pouvez d'autres mécanismes de communication inter-processus (IPC), tels que les sockets, ou Posix ou Mémoire partagée SysV. Mais vous serez limité aux interfaces et aux canaux IPC pris en charge par les programmes que vous appelez.
Généralement, on utilise n'importe quel subprocess
synchrone - il suffit d'appeler un utilitaire externe et de lire sa sortie ou d'attendre son achèvement (peut-être en lisant ses résultats à partir d'un fichier temporaire, ou après les avoir publiés dans une base de données).
Cependant, on peut engendrer des centaines de sous-processus et les interroger. C'est exactement ce que fait ma propre classe utilitaire préférée .
Le plus gros inconvénient du subprocess
module est que la prise en charge des E / S est généralement bloquante. Il y a un projet de PEP-3145 pour corriger cela dans une version future de Python 3.x et une alternative asyncproc (Avertissement qui mène directement au téléchargement, pas à une sorte de documentation ni à README). J'ai également trouvé qu'il est relativement facile d'importer fcntl
et de manipuler Popen
directement vos descripteurs de fichiers PIPE - même si je ne sais pas si cela est portable sur des plates-formes non-UNIX.
(Mise à jour: 7 août 2019: prise en charge de Python 3 pour les sous-processus ayncio : sous-processus asyncio )
subprocess
n'a presque pas de support de gestion d'événements ... bien que vous puissiez utiliser le signal
module et les signaux UNIX / Linux à l'ancienne - en tuant vos processus doucement, pour ainsi dire.
L' option multitraitement :
multiprocessing
est pour exécuter des fonctions dans votre code (Python) existant avec la prise en charge de communications plus flexibles entre cette famille de processus. En particulier, il est préférable de construire votre multiprocessing
IPC autour des Queue
objets du module lorsque cela est possible, mais vous pouvez également utiliser des Event
objets et diverses autres fonctionnalités (dont certaines sont vraisemblablement construites autour du mmap
support sur les plates-formes où ce support est suffisant).
Le multiprocessing
module de Python est destiné à fournir des interfaces et des fonctionnalités très similaires threading
tout en permettant à CPython de faire évoluer votre traitement entre plusieurs processeurs / cœurs malgré le GIL (Global Interpreter Lock). Il tire parti de tous les efforts de verrouillage et de cohérence SMP à granularité fine réalisés par les développeurs de votre noyau OS.
L' option de filetage :
threading
est destiné à une gamme assez restreinte d'applications qui sont liées aux E / S (n'ont pas besoin d'évoluer sur plusieurs cœurs de processeur) et qui bénéficient de la latence extrêmement faible et de la surcharge de commutation de la commutation de thread (avec mémoire centrale partagée) par rapport au processus / changement de contexte. Sur Linux, c'est presque l'ensemble vide (les temps de changement de processus Linux sont extrêmement proches de ses commutateurs de thread).
threading
souffre de deux inconvénients majeurs en Python .
L'un, bien sûr, est spécifique à l'implémentation --- affectant principalement CPython. C'est le GIL. Pour la plupart, la plupart des programmes CPython ne bénéficieront pas de la disponibilité de plus de deux processeurs (cœurs) et souvent les performances souffriront du conflit de verrouillage GIL.
Le problème le plus important qui n'est pas spécifique à l'implémentation est que les threads partagent la même mémoire, les mêmes gestionnaires de signaux, les descripteurs de fichiers et certaines autres ressources du système d'exploitation. Ainsi, le programmeur doit être extrêmement prudent sur le verrouillage des objets, la gestion des exceptions et d'autres aspects de leur code qui sont à la fois subtils et qui peuvent tuer, bloquer ou bloquer tout le processus (suite de threads).
Par comparaison, le multiprocessing
modèle donne à chaque processus sa propre mémoire, ses propres descripteurs de fichiers, etc. Un plantage ou une exception non gérée dans l'un d'entre eux ne fera que tuer cette ressource et gérer de manière robuste la disparition d'un processus enfant ou frère peut être considérablement plus facile que de déboguer, isoler et résoudre ou contourner des problèmes similaires dans les threads.
- (Remarque: l'utilisation de
threading
avec les principaux systèmes Python, tels que NumPy , peut souffrir beaucoup moins de conflits GIL que la plupart de votre propre code Python. C'est parce qu'ils ont été spécialement conçus pour le faire; les parties natives / binaires de NumPy, par exemple, publiera le GIL quand c'est sûr).
L' option tordue :
Il convient également de noter que Twisted offre une autre alternative à la fois élégante et très difficile à comprendre . Fondamentalement, au risque de trop simplifier au point où les fans de Twisted pourraient prendre d'assaut ma maison avec des fourches et des torches, Twisted fournit un multitâche coopératif axé sur les événements dans n'importe quel processus (unique).
Pour comprendre comment cela est possible, il faut lire les fonctionnalités de select()
(qui peuvent être construites autour de select () ou poll () ou des appels système similaires du système d'exploitation). Fondamentalement, tout est motivé par la possibilité de demander au système d'exploitation de se mettre en veille en attendant toute activité sur une liste de descripteurs de fichiers ou un délai d'expiration.
Le réveil de chacun de ces appels à select()
est un événement --- soit un événement impliquant une entrée disponible (lisible) sur un certain nombre de sockets ou de descripteurs de fichier, soit l'espace tampon devenant disponible sur certains autres descripteurs ou sockets (inscriptibles), certaines conditions exceptionnelles (TCP paquets PUSH hors bande, par exemple), ou un TIMEOUT.
Ainsi, le modèle de programmation Twisted est construit autour de la gestion de ces événements puis de la boucle sur le gestionnaire "principal" résultant, lui permettant de distribuer les événements à vos gestionnaires.
Personnellement, je pense au nom Twisted comme évocateur du modèle de programmation ... puisque votre approche du problème doit être, dans un certain sens, "tordue" à l'envers. Plutôt que de concevoir votre programme comme une série d'opérations sur les données d'entrée et les sorties ou les résultats, vous écrivez votre programme en tant que service ou démon et définissez comment il réagit à divers événements. (En fait, la "boucle principale" principale d'un programme Twisted est (généralement? Toujours?) A reactor()
).
Les principaux défis de l'utilisation de Twisted impliquent de tordre votre esprit autour du modèle événementiel et également d'éviter l'utilisation de bibliothèques de classes ou de boîtes à outils qui ne sont pas écrites pour coopérer dans le cadre Twisted. C'est pourquoi Twisted fournit ses propres modules pour la gestion du protocole SSH, pour les curses, et ses propres fonctions de sous-processus / Popen, et de nombreux autres modules et gestionnaires de protocole qui, à première vue, semblent dupliquer des éléments dans les bibliothèques standard Python.
Je pense qu'il est utile de comprendre Twisted à un niveau conceptuel même si vous n'avez jamais l'intention de l'utiliser. Il peut donner des informations sur les performances, les conflits et la gestion des événements dans votre threading, votre multitraitement et même la gestion de sous-processus ainsi que tout traitement distribué que vous entreprenez.
( Note: Les nouvelles versions de Python 3.x sont notamment asyncio (E / S asynchrones) caractéristiques telles que async def , le @ async.coroutine décorateur, et le await mot - clé, et le rendement du futur soutien Tous ces éléments sont à peu près semblables à. Tordu du point de vue du processus (multitâche coopératif)). (Pour connaître l'état actuel de la prise en charge de Twisted pour Python 3, consultez: https://twistedmatrix.com/documents/current/core/howto/python3.html )
L' option distribuée :
Un autre domaine du traitement sur lequel vous n'avez pas posé de questions, mais qui mérite d'être considéré, est celui du traitement distribué . Il existe de nombreux outils et frameworks Python pour le traitement distribué et le calcul parallèle. Personnellement, je pense que le plus simple à utiliser est celui qui est le moins souvent considéré comme étant dans cet espace.
Il est presque trivial de créer un traitement distribué autour de Redis . L'ensemble du magasin de clés peut être utilisé pour stocker des unités de travail et des résultats, les listes Redis peuvent être utilisées comme Queue()
un objet similaire et le support PUB / SUB peut être utilisé pour une Event
gestion similaire. Vous pouvez hacher vos clés et utiliser des valeurs, répliquées sur un cluster libre d'instances Redis, pour stocker la topologie et les mappages de jetons de hachage afin de fournir un hachage et un basculement cohérents pour évoluer au-delà de la capacité d'une seule instance pour coordonner vos travailleurs. et rassembler les données (pickled, JSON, BSON ou YAML) parmi eux.
Bien sûr, alors que vous commencez à créer une solution à plus grande échelle et plus sophistiquée autour de Redis, vous réinstallez de nombreuses fonctionnalités qui ont déjà été résolues à l'aide de Celery , Apache Spark et Hadoop , Zookeeper , etcd , Cassandra , etc. Ceux-ci ont tous des modules pour l'accès Python à leurs services.
[Mise à jour: quelques ressources à prendre en compte si vous envisagez d' utiliser Python pour un calcul intensif sur les systèmes distribués: IPython Parallel et PySpark . Bien qu'il s'agisse de systèmes informatiques distribués à usage général, ce sont des sous-systèmes de science et d'analyse des données particulièrement accessibles et populaires].
Conclusion
Vous avez là toute une gamme d'alternatives de traitement pour Python, allant du simple thread, avec de simples appels synchrones à des sous-processus, des pools de sous-processus interrogés, des threads et du multitraitement, le multitâche coopératif basé sur les événements et le traitement distribué.