Ich schreibe eine GUI-Anwendung, die regelmäßig Daten über eine Webverbindung abruft. Da dieser Abruf eine Weile dauert, reagiert die Benutzeroberfläche während des Abrufvorgangs nicht mehr (sie kann nicht in kleinere Teile aufgeteilt werden). Aus diesem Grund möchte ich die Webverbindung an einen separaten Arbeitsthread auslagern.
[Ja, ich weiß, jetzt habe ich zwei Probleme .]
Wie auch immer, die Anwendung verwendet PyQt4, daher möchte ich wissen, was die bessere Wahl ist: Verwenden Sie die Threads von Qt oder das Python- threading
Modul? Was sind die Vor- und Nachteile der einzelnen? Oder haben Sie einen ganz anderen Vorschlag?
Bearbeiten (erneutes Kopfgeld): Während die Lösung in meinem speziellen Fall wahrscheinlich darin besteht, eine nicht blockierende Netzwerkanforderung zu verwenden, wie von Jeff Ober und Lukáš Lalinský vorgeschlagen (so dass die Parallelitätsprobleme im Grunde der Netzwerkimplementierung überlassen bleiben ), möchte ich dennoch eine weitere ausführliche Antwort auf die allgemeine Frage:
Welche Vor- und Nachteile hat die Verwendung von PyQt4-Threads (dh Qt-Threads) gegenüber nativen Python-Threads (aus dem threading
Modul)?
Edit 2: Vielen Dank für Ihre Antworten. Obwohl es keine 100% ige Übereinstimmung gibt, scheint es einen weit verbreiteten Konsens darüber zu geben, dass die Antwort "Qt verwenden" lautet, da der Vorteil darin besteht, dass die Integration in den Rest der Bibliothek erfolgt und keine wirklichen Nachteile entstehen.
Für alle, die zwischen den beiden Threading-Implementierungen wählen möchten, empfehle ich dringend, alle hier bereitgestellten Antworten zu lesen, einschließlich des PyQt-Mailinglisten-Threads, auf den der Abt verweist.
Es gab mehrere Antworten, die ich für das Kopfgeld in Betracht gezogen hatte; Am Ende wählte ich Abt als sehr relevante externe Referenz; Es war jedoch ein enger Anruf.
Danke noch einmal.
quelle
QCoreApplication.postEvent
von einem Python-Thread mit einer Geschwindigkeit von 100 Mal pro Sekunde in einer Anwendung aufgerufen , die plattformübergreifend ausgeführt wird und seit 1000 Stunden getestet wurde. Ich habe noch nie Probleme damit gesehen. Ich denke, es ist in Ordnung, solange sich das Zielobjekt im MainThread oder einem QThread befindet. Ich habe es auch in eine schöne Bibliothek eingepackt , siehe qtutils .Pythons Threads werden einfacher und sicherer, und da es sich um eine E / A-basierte Anwendung handelt, können sie die GIL umgehen. Haben Sie in Betracht gezogen, nicht blockierende E / A mit Twisted- oder nicht blockierenden Sockets / Select zu blockieren?
EDIT: mehr zu Threads
Python-Threads
Pythons Threads sind System-Threads. Python verwendet jedoch eine globale Interpretersperre (GIL), um sicherzustellen, dass der Interpreter immer nur einen Block mit Bytecode-Anweisungen einer bestimmten Größe gleichzeitig ausführt. Glücklicherweise gibt Python die GIL während der Eingabe- / Ausgabeoperationen frei, wodurch Threads für die Simulation nicht blockierender E / A nützlich sind.
Wichtiger Hinweis: Dies kann irreführend sein, da die Anzahl der Bytecode-Anweisungen nicht der Anzahl der Zeilen in einem Programm entspricht. Selbst eine einzelne Zuweisung ist in Python möglicherweise nicht atomar, daher ist eine Mutex-Sperre für jeden Codeblock erforderlich , der auch mit der GIL atomar ausgeführt werden muss.
QT-Threads
Wenn Python die Kontrolle an ein kompiliertes Modul eines Drittanbieters übergibt, gibt es die GIL frei. Es liegt in der Verantwortung des Moduls, bei Bedarf die Atomizität sicherzustellen. Wenn die Kontrolle zurückgegeben wird, verwendet Python die GIL. Dies kann die Verwendung von Bibliotheken von Drittanbietern in Verbindung mit Threads verwirrend machen. Es ist noch schwieriger, eine externe Threading-Bibliothek zu verwenden, da dadurch die Unsicherheit darüber erhöht wird, wo und wann die Steuerung in den Händen des Moduls gegenüber dem Interpreter liegt.
QT-Threads arbeiten mit freigegebener GIL. QT-Threads können gleichzeitig QT-Bibliothekscode (und anderen kompilierten Modulcode, der die GIL nicht erfasst) ausführen. Der im Kontext eines QT-Threads ausgeführte Python-Code erhält jedoch weiterhin die GIL, und jetzt müssen Sie zwei Logiksätze zum Sperren Ihres Codes verwalten.
Am Ende sind sowohl QT-Threads als auch Python-Threads Wrapper um System-Threads. Python-Threads sind geringfügig sicherer zu verwenden, da diejenigen Teile, die nicht in Python geschrieben sind (implizit die GIL verwenden), auf jeden Fall die GIL verwenden (obwohl der obige Vorbehalt weiterhin gilt).
Nicht blockierende E / A.
Threads machen Ihre Anwendung außerordentlich komplex. Besonders wenn es um die bereits komplexe Interaktion zwischen dem Python-Interpreter und dem kompilierten Modulcode geht. Während es für viele schwierig ist, ereignisbasierte Programmierung zu befolgen, ist es oft weniger schwierig, über ereignisbasierte, nicht blockierende E / A nachzudenken als über Threads.
Mit asynchroner E / A können Sie immer sicher sein, dass der Ausführungspfad für jeden offenen Deskriptor konsistent und geordnet ist. Es gibt offensichtlich Probleme, die behoben werden müssen, z. B. was zu tun ist, wenn Code in Abhängigkeit von einem offenen Kanal weiter von den Ergebnissen des Codes abhängt, der aufgerufen werden soll, wenn ein anderer offener Kanal Daten zurückgibt.
Eine gute Lösung für ereignisbasierte, nicht blockierende E / A ist die neue Diesel- Bibliothek. Es ist momentan auf Linux beschränkt, aber es ist außerordentlich schnell und recht elegant.
Es lohnt sich auch, pyevent zu lernen , einen Wrapper um die wunderbare Libevent-Bibliothek, der ein grundlegendes Framework für die ereignisbasierte Programmierung mit der schnellsten verfügbaren Methode für Ihr System bietet (festgelegt zur Kompilierungszeit).
quelle
Der Vorteil von
QThread
ist, dass es in den Rest der Qt-Bibliothek integriert ist. Das heißt, thread-fähige Methoden in Qt müssen wissen, in welchem Thread sie ausgeführt werden, und um Objekte zwischen Threads zu verschieben, müssen Sie sie verwendenQThread
. Eine weitere nützliche Funktion ist das Ausführen einer eigenen Ereignisschleife in einem Thread.Wenn Sie auf einen HTTP-Server zugreifen, sollten Sie dies berücksichtigen
QNetworkAccessManager
.quelle
QNetworkAccessManager
sieht es vielversprechend aus. Vielen Dank.Ich habe mir dieselbe Frage gestellt, als ich bei PyTalk gearbeitet habe .
Wenn Sie Qt verwenden, müssen Sie verwenden
QThread
, um das Qt-Framework und insbesondere das Signal- / Slot-System verwenden zu können.Mit der Signal- / Slot-Engine können Sie von einem Thread zu einem anderen und mit jedem Teil Ihres Projekts sprechen.
Darüber hinaus gibt es keine sehr leistungsbedingte Frage zu dieser Auswahl, da beide C ++ - Bindungen sind.
Hier ist meine Erfahrung mit PyQt und Thread.
Ich ermutige Sie zu verwenden
QThread
.quelle
Jeff hat einige gute Punkte. Nur ein Hauptthread kann GUI-Updates durchführen. Wenn Sie die GUI innerhalb des Threads aktualisieren müssen, erleichtern die Verbindungssignale in der Warteschlange von Qt-4 das Senden von Daten über Threads und werden automatisch aufgerufen, wenn Sie QThread verwenden. Ich bin mir nicht sicher, ob dies der Fall sein wird, wenn Sie Python-Threads verwenden, obwohl es einfach ist, einen Parameter hinzuzufügen
connect()
.quelle
Ich kann es auch nicht wirklich empfehlen, aber ich kann versuchen, Unterschiede zwischen CPython- und Qt-Threads zu beschreiben.
Erstens werden CPython-Threads nicht gleichzeitig ausgeführt, zumindest nicht Python-Code. Ja, sie erstellen Systemthreads für jeden Python-Thread, es darf jedoch nur der Thread ausgeführt werden, der derzeit Global Interpreter Lock enthält (C-Erweiterungen und FFI-Code können dies umgehen, aber Python-Bytecode wird nicht ausgeführt, während der Thread keine GIL enthält).
Auf der anderen Seite haben wir Qt-Threads, die im Grunde eine gemeinsame Schicht über System-Threads sind, keine globale Interpreter-Sperre haben und daher gleichzeitig ausgeführt werden können. Ich bin mir nicht sicher, wie PyQt damit umgeht. Wenn Ihre Qt-Threads jedoch keinen Python-Code aufrufen, sollten sie gleichzeitig ausgeführt werden können (abgesehen von verschiedenen zusätzlichen Sperren, die möglicherweise in verschiedenen Strukturen implementiert sind).
Für eine zusätzliche Feinabstimmung können Sie die Anzahl der Bytecode-Anweisungen ändern, die vor dem Eigentümerwechsel von GIL interpretiert werden. Niedrigere Werte bedeuten mehr Kontextwechsel (und möglicherweise eine höhere Reaktionsfähigkeit), aber eine geringere Leistung pro einzelnem Thread (Kontextwechsel haben ihre Kosten - wenn Sie dies tun versuche alle paar Anweisungen zu wechseln, es hilft nicht bei der Geschwindigkeit.)
Hoffe es hilft bei deinen Problemen :)
quelle
Ich kann die genauen Unterschiede zwischen Python- und PyQt-Threads nicht kommentieren, aber ich habe das getan, was Sie versuchen
QThread
,QNetworkAcessManager
und sichergestellt, dass Sie aufrufen,QApplication.processEvents()
während der Thread aktiv ist. Wenn die Reaktionsfähigkeit der Benutzeroberfläche wirklich das Problem ist, das Sie lösen möchten, hilft das letztere.quelle
QNetworkAcessManager
benötigt keinen Thread oderprocessEvents
. Es werden asynchrone E / A-Operationen verwendet.QNetworkAcessManager
undhttplib2
. Mein asynchroner Code verwendethttplib2
.