it-swarm.com.de

Threading in einer PyQt-Anwendung: Verwenden Sie Qt-Threads oder Python Threads?

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, also würde ich gerne wissen, was die bessere Wahl ist: Verwenden Sie Qt-Threads oder verwenden Sie das Python threading -Modul? Was sind die Vor-/Nachteile von Oder haben Sie einen ganz anderen Vorschlag?

Bearbeiten (Gegenleistung): Während die Lösung in meinem speziellen Fall wahrscheinlich eine nicht blockierende Netzwerkanforderung wie Jeff Ober verwendet = und Lukáš Lalinský vorgeschlagen (also überlassen Sie die Probleme im Zusammenhang mit der Netzwerkimplementierung im Grunde genommen der Netzwerkimplementierung), möchte ich dennoch eine ausführlichere Antwort auf die allgemeine Frage:

Was sind die Vor- und Nachteile der Verwendung von PyQt4-Threads (d. H. Qt-Threads) gegenüber nativen Python Threads (aus dem Modul threading)?


Edit 2: Danke an alle für eure Antworten. Obwohl es keine hundertprozentige Übereinstimmung gibt, scheint es einen weitverbreiteten Konsens zu geben, dass die Antwort "use Qt" lautet, da der Vorteil darin besteht, in die restliche Bibliothek integriert zu werden, ohne dabei echte Nachteile zu verursachen.

Für alle, die zwischen den beiden Threading-Implementierungen wählen möchten, empfehle ich dringend, alle hier gegebenen Antworten zu lesen, einschließlich des PyQt-Mailinglisten-Threads, auf den Abt verweist.

Es gab mehrere Antworten, die ich für das Kopfgeld in Betracht gezogen habe; am ende habe ich abbots als sehr relevante externe referenz gewählt; Es war jedoch ein enger Anruf.

Danke noch einmal.

104
balpha

Dies war diskutiert vor nicht allzu langer Zeit in der PyQt-Mailingliste. Zitat von Giovanni Bajo Kommentare zum Thema:

Es ist meistens dasselbe. Der Hauptunterschied besteht darin, dass QThreads besser in Qt integriert sind (asynchrone Signale/Slots, Ereignisschleife usw.). Sie können Qt auch nicht von einem Python Thread aus verwenden (Sie können beispielsweise kein Ereignis über QApplication.postEvent in den Haupt-Thread posten): Sie benötigen einen QThread, damit dies funktioniert.

Eine allgemeine Faustregel könnte darin bestehen, QThreads zu verwenden, wenn Sie in irgendeiner Weise mit Qt interagieren möchten, und andernfalls Python Threads zu verwenden.

Und einige frühere Kommentare zu diesem Thema von PyQts Autor: "Sie sind beide Wrapper um die gleichen nativen Thread-Implementierungen." Und beide Implementierungen verwenden GIL auf dieselbe Weise.

99
abbot

Pythons Threads sind einfacher und sicherer. Da es sich um eine E/A-basierte Anwendung handelt, können sie die GIL umgehen. Haben Sie darüber nachgedacht, E/A mit verdrillten oder nicht blockierenden Sockets/select nicht zu blockieren?

EDIT: mehr zu Threads

Python-Threads

Pythons Threads sind System-Threads. Allerdings verwendet Python eine globale Interpretersperre (GIL), um sicherzustellen, dass der Interpreter immer nur einen bestimmten Größenblock von Bytecode-Anweisungen gleichzeitig ausführt. Zum Glück Python gibt die GIL während der Eingabe-/Ausgabeoperationen frei, wodurch Threads für die Simulation nicht blockierender E/A nützlich werden.

Wichtiger Hinweis: Dies kann irreführend sein, da die Anzahl der Bytecode-Anweisungen nicht entspricht die Anzahl der Zeilen in einem Programm. 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 Steuerung 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 Steuerung zurückgegeben wird, Python verwendet die GIL. Dies kann die Verwendung von Bibliotheken von Drittanbietern in Verbindung mit Threads verwirren. Es ist sogar noch schwieriger, eine externe Thread-Bibliothek zu verwenden, da die Kontrolle darüber, wo und wann sie sich befindet, ungewiss ist Modul gegen den Dolmetscher.

QT-Threads arbeiten mit der freigegebenen GIL. QT-Threads können gleichzeitig QT-Bibliothekscode (und anderen kompilierten Modulcode, der die GIL nicht erfasst) ausführen. Der im Rahmen eines QT-Threads ausgeführte Python= - Code erhält jedoch immer noch die GIL, und jetzt müssen Sie zwei Sätze von Logik zum Sperren Ihres Codes.

Letztendlich sind sowohl QT-Threads als auch Python - Threads Wrapper um System-Threads. Python - Threads sind etwas sicherer zu verwenden, da diese Teile nicht eingeschrieben sind Python (implizit mit der GIL) benutze die GIL auf jeden Fall (obwohl der oben genannte Vorbehalt immer noch zutrifft.)

Nicht blockierende E/A

Threads erhöhen die Komplexität Ihrer Anwendung außerordentlich. Vor allem, wenn es um die bereits komplexe Interaktion zwischen dem Python= Interpreter und kompiliertem Modulcode geht. Während viele es schwierig finden, ereignisbasierte Programmierung zu befolgen, ist ereignisbasierte, nicht blockierende E/A oft viel weniger schwer zu überlegen als Threads.

Mit asynchroner E/A können Sie immer sicher sein, dass der Ausführungspfad für jeden offenen Deskriptor konsistent und ordnungsgemäß ist. Es gibt offensichtlich Probleme, die behoben werden müssen, wie beispielsweise, was zu tun ist, wenn Code, der von einem offenen Kanal abhängt, 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. Momentan ist es auf Linux beschränkt, aber es ist außerordentlich schnell und recht elegant.

Es lohnt sich auch, sich mit pyevent vertraut zu machen, einem 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 bereitstellt (wird zur Kompilierungszeit festgelegt).

32
Jeff Ober

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 QThread verwenden. Eine weitere nützliche Funktion ist das Ausführen einer eigenen Ereignisschleife in einem Thread.

Wenn Sie auf einen HTTP-Server zugreifen, sollten Sie QNetworkAccessManager berücksichtigen.

21

Ich habe mir die gleiche Frage gestellt, als ich an PyTalk gearbeitet habe.

Wenn Sie Qt verwenden, müssen Sie QThread verwenden, 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 ist diese Auswahl nicht sehr leistungskritisch, da es sich bei beiden um C++ - Bindungen handelt.

Hier ist meine Erfahrung mit PyQt und Thread.

Ich empfehle Ihnen, QThread zu verwenden.

13
Natim

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 Warteschlangenverbindung -Signale 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, connect() einen Parameter hinzuzufügen.

9
Kaleb Pederson

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, jedenfalls nicht Python code. Ja, sie erstellen System-Threads für jeden Python thread, jedoch nur den Thread, der derzeit Global Interpreter Lock enthält, kann ausgeführt werden (C-Erweiterungen und FFI-Code umgehen ihn möglicherweise, aber Python Bytecode wird nicht ausgeführt, während der Thread GIL nicht enthält).

Auf der anderen Seite haben wir Qt-Threads, die im Grunde genommen eine gemeinsame Schicht über System-Threads sind, keine globale Interpreter-Sperre haben und daher gleichzeitig ausgeführt werden können. Ich bin nicht sicher, wie PyQt damit umgeht, aber es sei denn, Ihre Qt-Threads rufen Python Code) auf, sollten sie gleichzeitig ausgeführt werden können (abgesehen von verschiedenen zusätzlichen Sperren, die möglicherweise in verschiedenen Strukturen implementiert sind).

Für 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) Versuche alle paar Anweisungen zu wechseln, da dies nicht zu einer Geschwindigkeitsverbesserung führt.)

Hoffe es hilft bei deinen Problemen :)

5
p_l

Ich kann die genauen Unterschiede zwischen Python und PyQt-Threads nicht kommentieren, aber ich habe das getan, was Sie mit QThread, QNetworkAcessManager versuchen. ] und stellen Sie sicher, dass Sie QApplication.processEvents() aufrufen, 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 spätere .

0
brianz