GUI-Thread und Worker-Thread
Jedes Programm hat beim Start einen Thread. Dieser Thread wird in Qt-Anwendungen Hauptthread oder GUI-Thread genannt. Die Qt-GUI muss auf dem angegebenen Thread ausgeführt werden. Alle Widgets und einige ähnliche Klassen wie QPixmap funktionieren nicht in sekundären Threads. Ein sekundärer Thread wird normalerweise als Worker-Thread bezeichnet, der den Haupt-Thread des Programms entlasten soll.
Gleichzeitiger Zugriff auf Daten
Wenn zwei Threads einen Zeiger auf ein Objekt haben, können beide Threads irgendwann auf dieses Objekt zugreifen, was die Integrität des Objekts zerstören kann. Es ist notwendig, den gleichzeitigen Zugriff auf ein Objekt von verschiedenen Threads aus zu verhindern.
Anwendungsfälle
Es gibt zwei Hauptanwendungsfälle für Threads:
- Beschleunigung der Anwendung durch mehrere Computerprozessoren;
- Halten Sie die GUI für den Benutzer reaktionsfähig, wenn Prozesse lange laufen, die dazu führen können, dass die GUI der Anwendung blockiert.
Wann Thread-Alternativen verwendet werden sollten
Bevor Sie einen Thread erstellen, überlegen Sie, ob es nicht möglich ist, das Problem auf alternative Weise zu lösen.
Alternative | Beschreibung |
---|---|
QEventLoop::processEvents() | Das mehrmalige Aufrufen von QEventLoop::processEvents() während der Berechnung der Zeitkosten verhindert, dass die GUI abstürzt. Diese Lösung lässt sich jedoch nicht gut skalieren, da processEvents() je nach Hardwareplattform des Computers zu häufig oder zu selten aufgerufen werden kann oder nicht. |
QSocketNotifier QNetworkAccessManager QIODevice::readyRead() | Diese alternative Lösung hat sowohl einzelne als auch mehrere Threads, jeweils mit einer Lesesperre bei langsamen Verbindungen. Solange die Operationen für die Antwort schnell abgeschlossen werden können, ist dieses Design möglicherweise besser als die Verwendung zusätzlicher Threads. Dieses Design weist weniger Fehler auf und ist energieeffizienter als Streams. In vielen Fällen gibt es auch eine Leistungssteigerung. |
QTimer | Hintergrundprozesse können manchmal mit einem Timer ausgeführt werden, um nach einem Zeitplan ausgeführt zu werden. Beispielsweise wird ein gewisser Code mit einer gewissen Häufigkeit in einem speziellen Slot des Objekts ausgeführt. Ein Timer mit einem Wert von 0 wird immer dann ausgelöst, wenn keine anderen Thread-Ereignisse im Prozess vorhanden sind. |
Arten von Qt-Threads (QThread)
Thread-Lebenszyklus | Entwicklungsaufgabe | Lösung |
---|---|---|
Ein Anruf | Ausführen einer Methode in einem anderen Thread und Beenden des Threads, wenn die Methode abgeschlossen ist. | Qt bietet verschiedene Lösungen: * Eine Funktion schreiben und mit QtConcurrent::run() ausführen |
* Erben Sie eine Klasse von QRunnable und führen Sie sie in einem globalen Thread mit QThreadPool::globalInstance()->start() aus | ||
* Eine Klasse von QThread ableiten, die Methode QThread::run() überschreiben und sie mit der Methode QThread::start() verwenden. | ||
Ein Anruf | Die lange laufende Operation muss in einem anderen Thread platziert werden, und das Ergebnis der Verarbeitung muss an den GUI-Thread gesendet werden. | QThread wird verwendet, die Methode run() wird überschrieben und bei Bedarf wird ein Signal aufgerufen. Verbinden eines Signals mit dem GUI-Slot eines Threads unter Verwendung einer Warteschlange von Signal- und Slot-Verbindungen. |
Ein Anruf | Operationen müssen für alle Objekte aus der Liste durchgeführt werden. Die Verarbeitung muss unter Verwendung aller verfügbaren Kerne erfolgen. Ein typisches Beispiel ist das Zeichnen von Miniaturansichten von Bildern in einer Liste. | QtConcurrent stellt eine map() -Methode bereit, mit der Operationen für jedes Element ausgeführt werden können, filter() gibt die Elemente der Liste an, auf die Operationen angewendet werden. |
Dauerhaft | Es gibt ein Objekt, das in einem anderen Thread lebt und verschiedene Aufgaben auf Anfragen ausführt. Dies bedeutet, dass eine gewisse Kommunikation mit dem angegebenen Objekt erforderlich ist und ein Worker-Thread erforderlich ist. | Erben einer Klasse von QObject und Einfügen der erforderlichen Signale und Slots, Übergeben des Objekts an den Thread mit dem Start der Ereignisschleife und Kommunizieren mit dem Objekt über die Signal-/Slot-Warteschlange. |
Dauerhaft | Es gibt ein Objekt, das in einem anderen Thread lebt und sich wiederholende Aufgaben ausführt. | Es sieht aus wie ein Timer und auch die Verwendung von Signalen und Slots könnte funktionieren, aber es ist am besten, diesen Ansatz zu vermeiden. Beispiel: QSocketNotifier. |