Multithreading mit QNetworkAccessManager Konzeptfrage
-
Hallo,
ich möchte eine Klasse schreiben, die mir eine Datenbank mit Hilfe von einigen Anfragen an eine Web-API aufbaut. Das ganze sieht so aus, dass ich immer 200 Items aufeinmal anfragen kann und dann eine Antwort im JSON Format bekomme, das wird dann in meine Struktur übersetzt (der Teil steht schon) und anschließend werden die nächsten 200 Items angefragt.
Ich hatte diese Klasse zuvor in Delphi geschrieben, bin allerdings jetzt auf QT umgestiegen und stehe vor einem Problem. In Delphi hatte ich quasi eine Klasse die von QThread abgeleitet hatte (natürlich von dem Delphi Äquivalent) und die run-Methode implementiert hatte. Dort ist eine HTTP-GET Anfrage blockend und ich hatte im Prinzip einfach eine Schleife in der Run-Methode, die eben genauso lange gelaufen ist, bis ich alle (70.000) Items geladen hatte.
In QT ist der NetworkAccessManager ja asynchron und ich blicke deshalb leider absolut nichtmehr durch, wie ich es dort implementieren könnte.
Der Teil, der dann das JSON-Dokument in meine Datenstruktur packt ist natürlich blockend, ich denke auch dass das signal, was vom NetworkAccessManager emitted wird, wenn die Daten geladen wurde dann im Main-Thread ausgeführt werden, weswegen mein Programm dann sobald es ans Übersetzen der JSON-Datei geht freezen würde.
Könnt ihr mir eventuell helfen hier ein Konzept zu finden? Ich füge gleich hinzu, dass ich noch relativ neu in der QT-Welt bin
Vielen Dank für eure Antworten auf eine so ungewöhnliche Frage
EDIT: Also was ich erreichen möchte ist, dass der gesamte Rechenaufwand (Herunterladen+Parsen) in einem seperaten Thread läuft, ich aber trotzdem mit Signals mit dem Hauptthread kommunizieren möchte (zwecks Fortschrittsanzeige)
-
Hallo Frolo,
es gibt im Internet ein paar Implementierungen die den QNetworkAccessManager kapseln und aus einem Asychroen Aufruf einen Synchronen Aufruf machen. (z.B.: http://www.codeproject.com/Articles/484905/Use-QNetworkAccessManager-for-synchronous-download )
Bei Qt können Signale auch über Thread-Grenzen hinweg empfangen werden. Je nachdem wo das zu empfangene Objekt lebt (es muss nicht der Main-Thread sein).
Achja ich glaube nicht das es zielführend ist 200 Threads zu erzeugen, weil die DB das zulässt ...
Eventuell wäre auch das Qt Future Konzept was für dich.
Qt Threading (inkl. Future Konzept): https://www.youtube.com/watch?v=hUmkiLeWfJYViele Grüße,
Jakob
-
Hallo,
ich habe mich vorab schon ziemlich genau informiert und mir wurde in der Vergangenheit abgeraten den QNetworkAccessManager "synchron zu machen". Ich hatte damals vorschläge mit einem Eventloop bekommen.
Der Link sieht interessant aus, aber bevor ich mich dort einlese würde es mich auch interessieren, ob das mit der asynchronen Version auch funktioniert.
Nur eventuell noch wichtig: Ich plane nicht 200 Threads gleichzeitig zu betreiben sondern lediglich einen, der pro Schleifendurchlauf 200 Items als komplettes JSON-Dokument herunterläd und im Anschluss verarbeitet.
Also meine eigentliche Frage: Wenn ich nun eine Klasse von QThread ableite und in der neu implementierten run-Methode die get Methode (asynchron) des NetworkAccessManagers aufrufe, und außerdem das finished-Signal mit einem Slot in dieser neuen (eigenen Downloaderklasse) verbinde, in welchem Thread wird dann dieser Slot ausgeführt (welcher dann die ankommenden Daten verarbeiten würde und einen neuen Request starten).
In welchem Thread würde der Slot dann ausgeführt werden?
a) Im GUI Thread (besitzer meiner Downloadklasse welche von QThread ableitet)
b) Im Thread meiner Downloadklasse, da die ja der Besitzer des Networkaccessmanagers ist
c) In dem Thread den der NetworkAccessManager zum Downloaden verwendenVielen Dank für eure Hilfe!
a)
-
Du kannst es im Grunde genauso machen, wie in Delphi. Du leitest von QThread ab, implementierst run und führst das aus, was du ausführen musst. Den QNetworkAccessManager kannst du ganz einfach mit einer QEventLoop synchron machen. Das wäre eine brauchbare, funktionierende Lösung.
Das ganze könntest du wahrscheinlich auch ganz anders machen, aber obs besser wäre, kann ich jetzt auf den ersten Blick nicht beurteilen.
-
Ich habe gelesen, dass das mit dem Eventloop Probleme machen kann (wird ja auch in dem vorher geposteten Link erwähnt). Mich interessiert gerade eher die asynchrone Möglichkeit und dazu meine Frage, in welchem Thread der slot dann ausgeführt wird
-
In dem Codeproject Artikel wird erwähnt, dass das mit der QEventLoop in nicht GUI Threads zu Deadlocks führen wird. Das krieg man sicher in den Griff, sowas haben wir bei uns bestimmt irgendwo im Code.
Du kannst den Slot unterschiedlich connecten. Default ist auto connection, dann wird bei einem Aufruf im gleichen Thread eine Direct Connection ausgeführt, und wenn der Receiver in einem anderen Thread lebt, eine Queued Connection. Du kannst aber auch explizit den Connection Type angeben.
-
Könnt ihr mir ein kurzes Beispiel geben, wie ich das signal "finished" von dem NetworkAccessManager mit dem slot in meiner von QThread abgeleiteten Klasse verbinde, und zwar genau so, dass der slot der getriggert wird in dem Thread der eigenen Klasse läuft?
-
In der Qt Docu findest Du ein Beispiel:
http://doc.qt.io/qt-5/qthread.html