Multithreading-Technologien in Qt
Qt bietet viele Klassen und Funktionen für die Arbeit mit Threads. Im Folgenden werden vier verschiedene Ansätze vorgestellt, die Qt-Programmierer verwenden können, um Multithreading-Anwendungen zu implementieren.
QThread: Low-Level-API mit optionalen Ereignisschleifen
QThread ist die Grundlage der gesamten Thread-Kontrolle in Qt. Jede QThread Instanz repräsentiert und kontrolliert einen Thread.
QThread QThread kann entweder direkt instanziiert oder subclassed werden. Die Instanziierung einer QThread bietet eine parallele Ereignisschleife, die es ermöglicht, QObject Slots in einem sekundären Thread aufzurufen. Die Unterklassifizierung eines QThread ermöglicht es der Anwendung, den neuen Thread zu initialisieren, bevor die Ereignisschleife gestartet wird, oder parallelen Code ohne Ereignisschleife auszuführen.
Siehe QThread class reference und die Threading-Beispiele für Demonstrationen zur Verwendung von QThread.
QThreadPool und QRunnable: Wiederverwendung von Threads
Das häufige Erstellen und Zerstören von Threads kann teuer sein. Um diesen Overhead zu reduzieren, können bestehende Threads für neue Aufgaben wiederverwendet werden. QThreadPool ist eine Sammlung von wiederverwendbaren QThreads.
Um Code in einem der Threads von QThreadPool auszuführen, reimplementieren Sie QRunnable::run() und instanziieren Sie die Unterklasse QRunnable. Verwenden Sie QThreadPool::start(), um QRunnable in die Ausführungswarteschlange von QThreadPool zu stellen. Wenn ein Thread verfügbar wird, wird der Code in QRunnable::run() in diesem Thread ausgeführt.
Jede Qt-Anwendung hat einen globalen Thread-Pool, der über QThreadPool::globalInstance() zugänglich ist. Dieser globale Thread-Pool verwaltet automatisch eine optimale Anzahl von Threads, basierend auf der Anzahl der Kerne in der CPU. Ein separates QThreadPool kann jedoch explizit erstellt und verwaltet werden.
Qt Concurrent: Verwendung einer High-Level-API
Das Qt Concurrent Modul bietet High-Level-Funktionen, die sich mit einigen gängigen parallelen Berechnungsmustern befassen: map, filter und reduce. Im Gegensatz zur Verwendung von QThread und QRunnable erfordern diese Funktionen niemals die Verwendung von Low-Level-Threading-Primitiven wie Mutexe oder Semaphoren. Stattdessen geben sie ein QFuture -Objekt zurück, mit dem die Ergebnisse der Funktionen abgerufen werden können, wenn sie fertig sind. QFuture kann auch verwendet werden, um den Berechnungsfortschritt abzufragen und die Berechnung anzuhalten/fortzusetzen/abzubrechen. Der Einfachheit halber ermöglicht QFutureWatcher Interaktionen mit QFutureüber Signale und Slots.
Qt ConcurrentDie Map-, Filter- und Reduce-Algorithmen von verteilen die Berechnungen automatisch auf alle verfügbaren Prozessorkerne, so dass heute geschriebene Anwendungen auch dann noch skalierbar sind, wenn sie später auf einem System mit mehr Kernen eingesetzt werden.
Dieses Modul bietet auch die Funktion QtConcurrent::run(), mit der jede Funktion in einem anderen Thread ausgeführt werden kann. Allerdings unterstützt QtConcurrent::run() nur eine Teilmenge der Funktionen, die den Funktionen map, filter und reduce zur Verfügung stehen. Die Funktion QFuture kann verwendet werden, um den Rückgabewert der Funktion abzurufen und um zu prüfen, ob der Thread läuft. Ein Aufruf von QtConcurrent::run() verwendet jedoch nur einen Thread, kann nicht pausiert/fortgesetzt/abgebrochen werden und kann nicht nach dem Fortschritt abgefragt werden.
Siehe die Qt Concurrent Dokumentation des Moduls für Details zu den einzelnen Funktionen.
WorkerScript: Threading in QML
Mit dem QML-Typ WorkerScript kann JavaScript-Code parallel zum GUI-Thread ausgeführt werden.
Jeder WorkerScript -Instanz kann ein .js
-Skript zugeordnet werden. Wenn WorkerScript.sendMessage() aufgerufen wird, wird das Skript in einem separaten Thread (und einem separaten QML context) ausgeführt. Wenn das Skript seine Ausführung beendet hat, kann es eine Antwort an den GUI-Thread zurücksenden, der dann den WorkerScript.onMessage()-Signalhandler aufruft.
Die Verwendung von WorkerScript ist vergleichbar mit der Verwendung eines Workers QObject, der in einen anderen Thread verschoben wurde. Daten werden zwischen Threads über Signale übertragen.
Einzelheiten zur Implementierung des Skripts und eine Liste der Datentypen, die zwischen Threads übertragen werden können, finden Sie in der Dokumentation WorkerScript.
Auswahl eines geeigneten Ansatzes
Wie oben gezeigt, bietet Qt verschiedene Lösungen für die Entwicklung von threaded Anwendungen. Die richtige Lösung für eine bestimmte Anwendung hängt von dem Zweck des neuen Threads und der Lebensdauer des Threads ab. Im Folgenden finden Sie einen Vergleich der Threading-Technologien von Qt, gefolgt von empfohlenen Lösungen für einige Anwendungsbeispiele.
Vergleich der Lösungen
Merkmal | QThread | QRunnable und QThreadPool | QtConcurrent::run() | Qt Concurrent (Abbilden, Filtern, Reduzieren) | WorkerScript |
---|---|---|---|---|---|
Sprache | C++ | C++ | C++ | C++ | QML |
Thread-Priorität kann festgelegt werden | Ja | Ja | |||
Thread kann eine Ereignisschleife ausführen | Ja | ||||
Der Thread kann Datenaktualisierungen durch Signale empfangen | Ja (empfangen von einem Worker QObject) | Ja (empfangen von WorkerScript) | |||
Der Thread kann durch Signale gesteuert werden | Ja (empfangen von QThread) | Ja (empfangen von QFutureWatcher) | |||
Thread kann überwacht werden durch ein QFuture | Teilweise | Ja | |||
Eingebaute Möglichkeit zum Anhalten/Fortsetzen/Abbrechen | Ja |
Beispiel für Anwendungsfälle
Lebensdauer eines Threads | Vorgang | Lösung |
---|---|---|
Ein Aufruf | Ausführen einer neuen linearen Funktion innerhalb eines anderen Threads, optional mit Fortschrittsaktualisierung während des Laufs. | Qt bietet verschiedene Lösungen:
|
Ein Aufruf | Führen Sie eine bestehende Funktion in einem anderen Thread aus und erhalten Sie ihren Rückgabewert. | Führen Sie die Funktion mit QtConcurrent::run() aus. Lassen Sie QFutureWatcher das Signal finished() ausgeben, wenn die Funktion zurückgekehrt ist, und rufen Sie QFutureWatcher::result() auf, um den Rückgabewert der Funktion zu erhalten. |
Ein Aufruf | Führen Sie eine Operation für alle Elemente eines Containers aus, wobei alle verfügbaren Kerne verwendet werden. Zum Beispiel die Erstellung von Miniaturbildern aus einer Liste von Bildern. | Verwenden Sie die Funktion QtConcurrent::filter() von Qt Concurrent, um Containerelemente auszuwählen, und die Funktion QtConcurrent::map(), um eine Operation auf jedes Element anzuwenden. Um die Ausgabe in ein einziges Ergebnis zu falten, verwenden Sie stattdessen QtConcurrent::filteredReduced() und QtConcurrent::mappedReduced(). |
Ein Aufruf/Dauerhaft | Führen Sie eine lange Berechnung in einer reinen QML-Anwendung durch und aktualisieren Sie die grafische Benutzeroberfläche, wenn die Ergebnisse fertig sind. | Platzieren Sie den Berechnungscode in einem .js Skript und hängen Sie es an eine WorkerScript Instanz. Rufen Sie WorkerScript.sendMessage() auf, um die Berechnung in einem neuen Thread zu starten. Lassen Sie das Skript auch sendMessage() aufrufen, um das Ergebnis an den GUI-Thread zurückzugeben. Verarbeiten Sie das Ergebnis in onMessage und aktualisieren Sie die GUI dort. |
Dauerhaft | Ein Objekt, das in einem anderen Thread lebt, kann auf Anfrage verschiedene Aufgaben ausführen und/oder neue Daten erhalten, mit denen es arbeiten kann. | Erstellen Sie eine Unterklasse von QObject, um einen Worker zu erzeugen. Instanzieren Sie dieses Worker-Objekt und ein QThread. Verschieben Sie den Worker in den neuen Thread. Senden Sie Befehle oder Daten an das Worker-Objekt über Warteschlangen-Signal-Slot-Verbindungen. |
Dauerhaft | Wiederholte Ausführung einer teuren Operation in einem anderen Thread, der keine Signale oder Ereignisse empfangen muss. | Schreiben Sie die Endlosschleife direkt in eine Reimplementierung von QThread::run(). Starten Sie den Thread ohne eine Ereignisschleife. Lassen Sie den Thread Signale aussenden, um Daten an den GUI-Thread zurück zu senden. |
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.