J'écris une application GUI qui récupère régulièrement des données via une connexion Web. Étant donné que cette récupération prend un certain temps, l'interface utilisateur ne répond pas pendant le processus de récupération (elle ne peut pas être divisée en parties plus petites). C'est pourquoi j'aimerais externaliser la connexion Web vers un thread de travail distinct.
[Oui, je sais, maintenant j'ai deux problèmes .]
Quoi qu'il en soit, l'application utilise PyQt4, alors j'aimerais savoir quel est le meilleur choix: utiliser les threads de Qt ou utiliser le threading
module Python ? Quels sont les avantages / inconvénients de chacun? Ou avez-vous une suggestion totalement différente?
Edit (re bounty): Bien que la solution dans mon cas particulier utilisera probablement une demande de réseau non bloquante comme l' ont suggéré Jeff Ober et Lukáš Lalinský (donc en gros, en laissant les problèmes de concurrence à la mise en œuvre du réseau), j'aimerais toujours plus réponse approfondie à la question générale:
Quels sont les avantages et les inconvénients de l'utilisation des threads de PyQt4 (c'est-à-dire de Qt) par rapport aux threads Python natifs (à partir du threading
module)?
Edit 2: Merci à tous pour vos réponses. Bien qu'il n'y ait pas d'accord à 100%, il semble y avoir un large consensus sur le fait que la réponse est "utiliser Qt", puisque l'avantage de cela est l'intégration avec le reste de la bibliothèque, sans causer de réels inconvénients.
Pour tous ceux qui cherchent à choisir entre les deux implémentations de threads, je leur recommande vivement de lire toutes les réponses fournies ici, y compris le fil de discussion de la liste de diffusion PyQt vers lequel abbot est lié.
Il y avait plusieurs réponses que j'ai envisagées pour la prime; à la fin j'ai choisi l'abbé comme référence externe très pertinente; c'était, cependant, un appel serré.
Merci encore.
la source
QCoreApplication.postEvent
partir d'un thread Python à une vitesse de 100 fois par seconde, dans une application qui s'exécute sur plusieurs plates-formes et qui a été testée pendant des milliers d'heures. Je n'ai jamais vu de problèmes à ce sujet. Je pense que c'est bien tant que l'objet de destination est situé dans le MainThread ou un QThread. Je l'ai également emballé dans une belle bibliothèque, voir qtutils .Les threads de Python seront plus simples et plus sûrs, et comme il s'agit d'une application basée sur les E / S, ils sont capables de contourner le GIL. Cela dit, avez-vous envisagé des E / S non bloquantes en utilisant des sockets / select torsadés ou non bloquants?
EDIT: plus sur les fils
Fils Python
Les threads de Python sont des threads système. Cependant, Python utilise un verrou d'interpréteur global (GIL) pour s'assurer que l'interpréteur n'exécute qu'un bloc d'une certaine taille d'instructions de code d'octet à la fois. Heureusement, Python publie le GIL pendant les opérations d'entrée / sortie, ce qui rend les threads utiles pour simuler des E / S non bloquantes.
Attention importante: cela peut être trompeur, car le nombre d'instructions d'octet-code ne correspond pas au nombre de lignes dans un programme. Même une seule affectation peut ne pas être atomique en Python, donc un verrou mutex est nécessaire pour tout bloc de code qui doit être exécuté de manière atomique, même avec le GIL.
Fils QT
Lorsque Python transfère le contrôle à un module compilé tiers, il libère le GIL. Il devient de la responsabilité du module d'assurer l'atomicité le cas échéant. Lorsque le contrôle est renvoyé, Python utilisera le GIL. Cela peut rendre l'utilisation de bibliothèques tierces en conjonction avec des threads déroutante. Il est encore plus difficile d'utiliser une bibliothèque de threads externe car cela ajoute une incertitude quant à l'endroit et au moment où le contrôle est entre les mains du module par rapport à l'interpréteur.
Les threads QT fonctionnent avec le GIL publié. Les threads QT sont capables d'exécuter le code de la bibliothèque QT (et tout autre code de module compilé qui n'acquiert pas le GIL) simultanément. Cependant, le code Python exécuté dans le contexte d'un thread QT acquiert toujours le GIL, et vous devez maintenant gérer deux ensembles de logique pour verrouiller votre code.
En fin de compte, les threads QT et les threads Python sont des wrappers autour des threads système. Les threads Python sont légèrement plus sûrs à utiliser, car les parties qui ne sont pas écrites en Python (en utilisant implicitement le GIL) utilisent le GIL dans tous les cas (bien que la mise en garde ci-dessus s'applique toujours.)
E / S non bloquantes
Les threads ajoutent une complexité extraordinaire à votre application. Surtout lorsqu'il s'agit de l'interaction déjà complexe entre l'interpréteur Python et le code du module compilé. Alors que beaucoup trouvent la programmation basée sur les événements difficile à suivre, les E / S basées sur les événements et non bloquantes sont souvent beaucoup moins difficiles à raisonner que les threads.
Avec les E / S asynchrones, vous pouvez toujours être sûr que, pour chaque descripteur ouvert, le chemin d'exécution est cohérent et ordonné. Il y a, évidemment, des problèmes qui doivent être résolus, tels que ce qu'il faut faire lorsque le code dépendant d'un canal ouvert dépend en outre des résultats du code à appeler lorsqu'un autre canal ouvert renvoie des données.
La nouvelle bibliothèque Diesel est une solution intéressante pour les E / S non bloquantes basées sur les événements . Il est limité à Linux pour le moment, mais il est extraordinairement rapide et assez élégant.
Cela vaut également la peine d'apprendre pyevent , un wrapper autour de la merveilleuse bibliothèque libevent, qui fournit un cadre de base pour la programmation événementielle en utilisant la méthode la plus rapide disponible pour votre système (déterminée au moment de la compilation).
la source
L'avantage de
QThread
est qu'il est intégré au reste de la bibliothèque Qt. Autrement dit, les méthodes prenant en charge les threads dans Qt devront savoir dans quel thread elles s'exécutent et pour déplacer des objets entre les threads, vous devrez utiliserQThread
. Une autre fonctionnalité utile consiste à exécuter votre propre boucle d'événements dans un thread.Si vous accédez à un serveur HTTP, vous devriez envisager
QNetworkAccessManager
.la source
QNetworkAccessManager
semble prometteur. Merci.Je me suis posé la même question lorsque je travaillais pour PyTalk .
Si vous utilisez Qt, vous devez l'utiliser
QThread
pour pouvoir utiliser le framework Qt et en particulier le système signal / slot.Avec le moteur signal / slot, vous pourrez parler d'un thread à l'autre et avec chaque partie de votre projet.
De plus, il n'y a pas de très question de performance sur ce choix puisque les deux sont des liaisons C ++.
Voici mon expérience de PyQt et du thread.
Je vous encourage à utiliser
QThread
.la source
Jeff a quelques bons points. Un seul thread principal peut effectuer des mises à jour de l'interface graphique. Si vous avez besoin de mettre à jour l'interface graphique à partir du thread, les signaux de connexion en file d' attente de Qt-4 facilitent l'envoi de données entre les threads et seront automatiquement appelés si vous utilisez QThread; Je ne sais pas s'ils le seront si vous utilisez des threads Python, bien qu'il soit facile d'ajouter un paramètre à
connect()
.la source
Je ne peux pas vraiment recommander non plus, mais je peux essayer de décrire les différences entre les threads CPython et Qt.
Tout d'abord, les threads CPython ne s'exécutent pas simultanément, du moins pas le code Python. Oui, ils créent des threads système pour chaque thread Python, mais seul le thread qui détient actuellement Global Interpreter Lock est autorisé à s'exécuter (les extensions C et le code FFI peuvent le contourner, mais le bytecode Python n'est pas exécuté tant que le thread ne contient pas GIL).
D'un autre côté, nous avons des threads Qt, qui sont fondamentalement une couche commune sur les threads système, n'ont pas Global Interpreter Lock, et sont donc capables de fonctionner simultanément. Je ne sais pas comment PyQt le gère, mais à moins que vos threads Qt n'appellent du code Python, ils devraient pouvoir s'exécuter simultanément (à l'exception de divers verrous supplémentaires qui pourraient être implémentés dans diverses structures).
Pour un réglage plus fin, vous pouvez modifier la quantité d'instructions de bytecode qui sont interprétées avant de changer de propriétaire de GIL - des valeurs inférieures signifient plus de changement de contexte (et éventuellement une plus grande réactivité) mais des performances inférieures par thread individuel (les changements de contexte ont leur coût - si vous essayez de changer toutes les quelques instructions, cela n'aide pas à accélérer.)
J'espère que cela vous aidera à résoudre vos problèmes :)
la source
Je ne peux pas commenter les différences exactes entre les threads Python et PyQt, mais je fais ce que vous essayez de le faire en utilisant
QThread
,QNetworkAcessManager
et en veillant à l' appelQApplication.processEvents()
alors que le fil est vivant. Si la réactivité de l'interface graphique est vraiment le problème que vous essayez de résoudre, ce dernier vous aidera.la source
QNetworkAcessManager
ne nécessite pas de thread ouprocessEvents
. Il utilise des opérations d'E / S asynchrones.QNetworkAcessManager
ethttplib2
. Mon code asynchrone utilisehttplib2
.