Vous avez déjà reçu une grande variété de réponses, allant des "faux fils" aux cadres externes, mais je n'ai vu personne mentionner Queue.Queue
-- la "sauce secrète" du threading de CPython.
Pour développer : tant que vous n'avez pas besoin de superposer des traitements lourds pour le CPU en Python pur (dans ce cas, vous avez besoin de multiprocessing
-- mais ça vient avec ses propres Queue
aussi, donc vous pouvez, avec quelques précautions nécessaires, appliquer les conseils généraux que je donne;-), l'implémentation intégrée de Python threading
fera l'affaire... mais il le fera bien mieux si vous l'utilisez à bon escient par exemple, comme suit.
"Oubliez la mémoire partagée, censée être le principal avantage du threading par rapport au multiprocessing - elle ne fonctionne pas bien, ne s'adapte pas bien, n'a jamais fonctionné et ne fonctionnera jamais. N'utilisez la mémoire partagée que pour les structures de données qui sont mises en place une seule fois. avant vous créez des sous-filières et ne changez plus rien par la suite - pour tout le reste, créez une simple responsable de cette ressource, et communiquer avec ce thread par l'intermédiaire de Queue
.
Consacrez un thread spécialisé à chaque ressource que vous penseriez normalement protéger par des verrous : une structure de données mutable ou un groupe cohésif de celles-ci, une connexion à un processus externe (une BD, un serveur XMLRPC, etc.), un fichier externe, etc. Mettez en place un petit pool de threads pour les tâches d'usage général qui n'ont pas ou n'ont pas besoin d'une ressource dédiée de ce type Ne le fais pas. Créez des threads au fur et à mesure de vos besoins, sinon vous serez submergé par la surcharge du changement de thread.
La communication entre deux threads se fait toujours via Queue.Queue
-- une forme de passage de messages, la seule base saine pour le multitraitement (à part la mémoire transactionnelle, qui est prometteuse mais pour laquelle je ne connais aucune implémentation digne de ce nom, sauf en Haskell).
Chaque thread dédié gérant une seule ressource (ou un petit ensemble cohésif de ressources) écoute les demandes sur une instance spécifique de Queue.Queue. Les threads d'un pool attendent sur une seule instance partagée de Queue.Queue (Queue est solidement threadsafe et ne le fera pas vous faire défaut en la matière).
Les threads qui ont juste besoin de mettre en file d'attente une requête sur une file d'attente (partagée ou dédiée) le font sans attendre de résultats, et passent à autre chose. Les threads qui ont éventuellement besoin d'un résultat ou d'une confirmation pour une requête mettent en file d'attente une paire (requête, file d'attente de réception) avec une instance de Queue.Queue qu'ils viennent de créer, et finalement, quand la réponse ou la confirmation est indispensable pour continuer, ils obtiennent (en attendant) de leur file d'attente de réception. Assurez-vous d'être prêt à recevoir des réponses d'erreur ainsi que de vraies réponses ou confirmations (la fonction deferred
sont excellents pour organiser ce type de réponse structurée, d'ailleurs !).
Vous pouvez également utiliser la file d'attente pour "parquer" des instances de ressources qui peuvent être utilisées par n'importe quel thread mais qui ne peuvent jamais être partagées entre plusieurs threads à la fois (connexions DB avec certains composants DBAPI, curseurs avec d'autres, etc) -- cela vous permet de relâcher l'exigence de threads dédiés en faveur d'une plus grande mise en commun (un thread de pool qui reçoit de la file d'attente partagée une requête nécessitant une ressource pouvant être mise en file d'attente obtiendra cette ressource de la file d'attente appropriée, en attendant si nécessaire, etc etc).
Twisted est en fait un bon moyen d'organiser ce menuet (ou cette danse carrée, selon le cas), non seulement grâce aux deferreds, mais aussi en raison de son architecture de base saine, solide et hautement évolutive : vous pouvez organiser les choses de manière à utiliser des threads ou des sous-processus uniquement lorsque cela est vraiment justifié, tout en faisant la plupart des choses normalement considérées comme méritant un thread dans un seul thread événementiel.
Mais je réalise que Twisted n'est pas pour tout le monde - l'approche "dédier ou mettre en commun des ressources, utiliser les files d'attente à fond, ne jamais faire quoi que ce soit qui nécessite un verrou ou, à l'insu de Guido, une procédure de synchronisation encore plus avancée, comme un sémaphore ou une condition" peut toujours être utilisée même si vous ne pouvez pas vous faire à l'idée des méthodologies événementielles asynchrones, et elle offrira toujours plus de fiabilité et de performance que toute autre approche de threading largement applicable sur laquelle je suis tombé.
0 votes
Je vois au moins trois raisons pour lesquelles cette question (opinion, recommandation et trop large) doit être mise en attente.
1 votes
Je sais que cette question date de 09 mais quelqu'un peut-il y répondre avec
asyncio
s'il vous plaît ? Merci d'avance et j'aimerais bien voir à quoi ressemblerait le code.0 votes
@lpapp où la question a généré quelques réponses d'opinion, je crois que la formulation de la question est objective (trop large, peut-être). Le PO demande quels modules sont utilisés en Python pour la concurrence, et les avantages et inconvénients de chacun. Cela me semble raisonnable.