Filetage dans une application PyQt: utilisez des fils QT ou des fils Python?

j'écris une application GUI qui récupère régulièrement des données via une connexion web. Comme cette récupération prend un certain temps, L'iu ne réagit pas pendant le processus de récupération (il ne peut pas être divisé en parties plus petites). C'est pourquoi j'aimerais externaliser la connexion web à un thread de worker séparé.

[Oui, je sais, maintenant j'ai deux problèmes .]

quoi qu'il en soit, L'application utilise PyQt4, donc j'aimerais vous savez quel est le meilleur choix: Utiliser les threads de Qt ou utiliser le module Python threading ? Quels sont les avantages / inconvénients de chacun? Ou avez-vous une autre suggestion?

Edit (re bounty): alors que la solution dans mon cas particulier sera probablement en utilisant une requête réseau non-bloquante comme Jeff Ober et Lukáš Lalinský suggéré (donc essentiellement en laissant les problèmes de concurrence à la mise en réseau), je voudrais encore une réponse plus approfondie à la question générale:

Quels sont les avantages et les inconvénients d'utiliser les threads de PyQt4 (c.-à-d. Qt) sur les threads Python natifs (à partir du module threading )?


Edit 2: Merci à tous pour vous 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", étant donné que le l'avantage de cela est l'intégration avec le reste de la bibliothèque, tout en ne provoquant pas d'inconvénients réels.

pour tous ceux qui cherchent à choisir entre les deux implémentations de threading, je recommande fortement qu'ils lisent toutes les réponses fournies ici, y compris le fil de la liste de diffusion de PyQt que abbot lie.

il y avait plusieurs réponses que j'ai considérées pour la prime; à la fin j'ai choisi abbot pour la référence externe très pertinente; il s'agit toutefois d'une décision prise de justesse.

Merci encore.

96
demandé sur Community 2009-10-20 19:54:19

7 réponses

C'était discuté il n'y a pas si longtemps dans la liste de diffusion de PyQt. Citant Giovanni Bajo commente sur le sujet:

c'est presque pareil. La principale différence est que les QThreads sont meilleurs intégré avec Qt (signaux asynchrones/slots, boucle d'événement, etc.). De plus, vous ne pouvez pas utiliser Qt à partir d'un thread Python (vous ne pouvez pas par exemple post-événement pour le thread principal par le biais de QApplication.postEvent): vous j'ai besoin d'un QThread pour que ça marche.

une règle générale de base pourrait être D'utiliser QThreads si vous allez interagir d'une manière ou d'une autre avec Qt, et utiliser les threads Python autrement.

et quelques commentaires antérieurs à ce sujet de L'auteur de PyQt:"ils sont tous les deux enveloppants autour des mêmes implémentations de thread natif". Et les deux implémentations utilisent GIL de la même manière.

89
répondu abbot 2012-04-03 15:33:38

les threads de Python seront plus simples et plus sûrs, et puisque c'est pour une application basée sur les e/s, ils sont capables de contourner la GIL. Cela dit, Avez-vous envisagé de ne pas bloquer l'entrée/sortie en utilisant des sockets torsadés ou non bloquants/select?

EDIT: plus sur les threads

Python threads

les threads de Python sont des threads système. Cependant, Python utilise un mondial interprète de verrouillage (GIL) assurez-vous que l'interprète n'exécute qu'un bloc de taille d'instructions de code octet à la fois. Heureusement, Python libère la GIL pendant les opérations d'entrée / sortie, ce qui rend les threads utiles pour simuler des I /O non bloquants.

avertissement Important: ceci peut être trompeur, puisque le nombre d'instructions de byte-code ne pas correspondent au nombre de lignes dans un programme. Même une seule tâche peut ne pas être atomique dans Python, donc une serrure mutex est nécessaire pour n'importe quel "bloc de code 1519160920" qui doit être exécuté atomiquement, même avec la GIL.

QT threads

lorsque Python passe le contrôle à un module compilé par une tierce partie, il libère le GIL. Il incombe au module d'assurer l'atomicité lorsque cela est nécessaire. Quand le contrôle est passé en arrière, Python utilisera la GIL. Cela peut rendre l'utilisation de bibliothèques de tiers dans la conjonction avec les fils déroutant. Il est encore plus difficile d'utiliser une bibliothèque de threading externe parce qu'elle ajoute de l'incertitude quant à l'endroit et au moment où le contrôle est dans les mains du module par rapport à l'interpréteur.

QT threads fonctionnent avec le GIL libéré. Les threads QT sont capables d'exécuter simultanément le code de bibliothèque QT (et tout autre code de module compilé qui n'acquiert pas le GIL). Cependant, le code Python exécuté dans le contexte D'un thread QT reste acquiert la GIL, et maintenant vous devez gérer deux ensembles de logique pour verrouiller votre code.

au final, les threads QT et Python sont tous deux des enveloppements autour des threads système. Les threads Python sont légèrement plus sûrs à utiliser, puisque les parties qui ne sont pas écrites en Python (implicitement en utilisant la GIL) utilisent la GIL dans tous les cas (bien que la mise en garde ci-dessus s'applique toujours.)

Non-blocking I /O

Les fils

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, basée sur les événements, Les e/s ne bloquant pas est souvent beaucoup moins difficile à raisonner que les threads.

avec des entrées / sorties asynchrones, vous pouvez toujours être sûr que, pour chaque descripteur ouvert, le chemin d'exécution est cohérent et ordonnée. Il y a, évidemment, des questions qui doivent être abordées, comme ce qu'il faut faire lorsque le code dépendant d'un canal ouvert dépend davantage des résultats du code à appeler quand un autre canal ouvert renvoie des données.

la nouvelle bibliothèque Diesel est une bonne solution pour les event-based, non-blocking I/O. Il est limité à Linux pour le moment, mais il est extrêmement rapide et très élégant.

il vaut également votre temps pour apprendre pyevent , un wrapper autour de la bibliothèque libevent merveilleux, qui fournit un cadre de base pour la programmation basée sur l'événement en utilisant la méthode la plus rapide Disponible pour votre système (déterminé au moment de compiler).

30
répondu Jeff Ober 2009-10-29 11:52:07

L'avantage "d'151900920" est qu'il est intégré avec le reste de la bibliothèque Qt. C'est-à-dire que les méthodes de thread-aware dans Qt devront savoir dans quel thread elles s'exécutent, et pour déplacer des objets entre les threads, vous devrez utiliser QThread . Une autre fonctionnalité utile est d'exécuter votre propre boucle d'événement dans un thread.

si vous accédez à un serveur HTTP, vous devez considérer QNetworkAccessManager .

21
répondu Lukáš Lalinský 2009-10-20 16:11:23

je me suis posé la même question quand je travaillais à PyTalk .

si vous utilisez Qt, vous devez utiliser QThread pour pouvoir utiliser le cadre Qt et en particulier le système signal/slot.

avec le moteur signal/slot, vous serez en mesure de parler d'un fil à l'autre et avec chaque partie de votre projet.

en outre, il n'y a pas très question de performance de ce choix puisque les deux sont des fixations C++.

Voici mon expérience de PyQt et de thread.

je vous encourage à utiliser QThread .

13
répondu Natim 2009-10-27 01:17:08

Jeff a de bons arguments. Un seul thread principal peut effectuer des mises à jour de L'interface graphique. Si vous devez mettre à jour l'interface graphique à partir du thread, les signaux de Qt-4 "file d'attente facilitent l'envoi de données à travers les threads et seront automatiquement invoqués si vous utilisez QThread; Je ne suis pas sûr qu'ils le seront si vous utilisez des threads Python, bien qu'il soit facile d'ajouter un paramètre à connect() .

9
répondu Kaleb Pederson 2017-01-19 17:29:36

Je ne peux pas vraiment recommander non plus, mais je peux essayer de décrire les différences entre les fils CPython et Qt.

tout d'abord, les threads CPython ne s'exécutent pas simultanément, du moins pas en code Python. Oui, ils créent des threads système pour chaque thread Python, mais seul le thread qui détient actuellement le verrouillage global de L'interpréteur est autorisé à fonctionner (les extensions C et le code FFI peuvent le contourner, mais le bytecode Python n'est pas exécuté alors que le thread ne supporte pas GIL).

d'un autre côté, nous avons des threads Qt, qui sont essentiellement une couche commune sur les threads système, n'ont pas de verrouillage D'interpréteur Global, et sont donc capables d'exécuter simultanément. Je ne suis pas sûr de la façon dont PyQt s'en occupe, cependant à moins que vos threads Qt n'appellent du code Python, ils devraient pouvoir s'exécuter simultanément (à moins que diverses serrures supplémentaires ne soient implémentées dans diverses structures).

pour plus de précision, vous pouvez modifier la quantité d'instructions de bytecode qui sont interprété avant de changer de propriétaire de Gil - des valeurs plus basses signifient plus de commutation de contexte (et peut-être une réponse plus élevée) mais des performances plus faibles par thread individuel (les commutateurs de contexte ont leur coût-si vous essayez de changer toutes les instructions, il n'aide pas la vitesse.)

Espérons qu'il aidera à résoudre votre problème :)

5
répondu p_l 2009-10-28 09:15:37

Je ne peux pas commenter les différences exactes entre les threads Python et PyQt, mais j'ai fait ce que vous essayez de faire en utilisant QThread , QNetworkAcessManager et en m'assurant d'appeler QApplication.processEvents() pendant que le thread est vivant. Si la réactivité GUI est vraiment le problème que vous essayez de résoudre, plus tard aidera.

0
répondu brianz 2009-10-25 05:24:57