QThread: Funktionsweise
-
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Sorry dass ich nicht mehr geantwortet habe... war am Sachsenring unterwegs (die Ohren klingeln jetzt noch)
hth!
Ja... selbstverständlich. Der Slot deines Parsers soll ja via Eventloop des Threads (exec() in der Default-run-Methode) aufgerufen werden.Illuminatus hat geschrieben:Muss man den Thread Threadkontext (QThread *mThread) auch starten?
Nein, du hast das schon richtig verstanden. Du bist auch nicht der einzige, den das verwirrt.. Das Problem ist, dass hier die Doku von der Meinung einiger Entwickler bei Nokia abweicht.Illuminatus hat geschrieben:Ja aber laut dem Post vor einigen Tagen, is das der "falsche weg" einen thread zu implementieren oder hab ich das falsch verstanden??? Ich kann die doku nicht mit dem movetothread zusammenbringen
hth!
Ich habe ähnliche Probleme wie Illuminatus.
Ich habe mir auch den "richtigen" Weg zur Erstellung eines separaten Threads durchgelesen und habe es wie folgt realisiert:
Threadtest_5.h:
Threadtest_5.cpp:
Worker.h:
Worker.cpp:
Allerdings springt er bei mir gar nicht erst in die while(true) Schleife hinein. Auch wenn ich nach dem connect() mit context->start() die Eventloop starte ändert sich nichts am Verhalten.
Wenn ich das SIGNAL(clicked()) zunächst auf eine Funktion in Threadtest_5 lege und von da aus die doWork()-Funktion aufrufe springt er zwar in die Schleife, meine GUI ist danach aber eingefroren.
Des Weiteren ist die Frage nach dem Verbleib des Worker-Objekts (keine Ableitung der QThread-Klasse sondern eine Ableitung der QObject-Klasse) noch nicht genau beantwortet. Dies würde mich aber auch noch interessieren.
Wenn mich einer darüber aufklären könnte, wäre ich demjenigen sehr dankbar.
Johannes
Ich habe mir auch den "richtigen" Weg zur Erstellung eines separaten Threads durchgelesen und habe es wie folgt realisiert:
Threadtest_5.h:
Code: Alles auswählen
#ifndef THREADTEST_5_H
#define THREADTEST_5_H
#include <QtGui/QMainWindow>
#include "ui_threadtest_5.h"
#include <QThread>
#include "Worker.h"
class Threadtest_5 : public QMainWindow
{
Q_OBJECT
public:
Threadtest_5(QWidget *parent = 0, Qt::WFlags flags = 0);
~Threadtest_5();
QThread * context;
Worker * worker;
private:
Ui::Threadtest_5Class ui;
};
#endif // THREADTEST_5_H
Code: Alles auswählen
Threadtest_5::Threadtest_5(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
context = new QThread();
worker = new Worker();
worker->moveToThread(context);
connect(ui.pb_Start, SIGNAL(clicked()), worker, SLOT(doWork()));
//context->start();
}
Threadtest_5::~Threadtest_5()
{
}
Code: Alles auswählen
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class Worker :
public QObject
{
public:
Worker(void);
~Worker(void);
public slots:
void doWork();
};
#endif
Code: Alles auswählen
#include "Worker.h"
Worker::Worker(void)
{
}
Worker::~Worker(void)
{
}
void Worker::doWork()
{
while(true);
}
Wenn ich das SIGNAL(clicked()) zunächst auf eine Funktion in Threadtest_5 lege und von da aus die doWork()-Funktion aufrufe springt er zwar in die Schleife, meine GUI ist danach aber eingefroren.
Des Weiteren ist die Frage nach dem Verbleib des Worker-Objekts (keine Ableitung der QThread-Klasse sondern eine Ableitung der QObject-Klasse) noch nicht genau beantwortet. Dies würde mich aber auch noch interessieren.
Wenn mich einer darüber aufklären könnte, wäre ich demjenigen sehr dankbar.
Johannes
- Dateianhänge
-
- Threadtest.7z
- (1.13 KiB) 98-mal heruntergeladen
Das liegt daran, dass die Funktion in dem Thread abgearbeitet wird, in dem sie aufgerufen wurde. Soll heißen: Dein clicked() ruft den SLOT von Threadtest_-Objekt im Mainthread auf. In dem SLOT rufst du doWork() auf, doWork läuft damit im MainThread - und blockiert die Gui.MisterJ hat geschrieben:Wenn ich das SIGNAL(clicked()) zunächst auf eine Funktion in Threadtest_5 lege und von da aus die doWork()-Funktion aufrufe springt er zwar in die Schleife, meine GUI ist danach aber eingefroren.
Du musst jetzt nur schauen, dass der SLOT doWork() im neuen Thread ausgeführt wird - eine einfach AutoConnection tut es da (wie du es im Code auch stehen hast).
Dein Problem: Du startest den context-QThread nicht (warum ist das eigentlich auskommentiert?). Mach das so
Code: Alles auswählen
worker->moveToThread(context);
context->start();
connect(ui.pb_Start, SIGNAL(clicked()), worker, SLOT(doWork()));
Hallo franzf,
danke für die Antwort. Ich hatte an meinen letzten Beitrag schon die Dateien meines Minimalbeispiels angehängt:
threadtest_5.ui
main.cpp
threadtest_5.h
threadtest_5.cpp
Worker.h
Worker.cpp
Reichen diese aus?
Ich habe die Auskommentierung der context->start()-Funktion wieder rückgängig gemacht und auch an genau die Stelle, die du mir beschrieben hast, gesetzt. Trotzdem wird die Schleife nicht ausgeführt. Die Änderung habe ich jetzt nicht noch einmal neu hochgeladen, daher ist dies in der threadtest_5.cpp noch nicht so implementiert.
Viele Grüße,
Johannes
danke für die Antwort. Ich hatte an meinen letzten Beitrag schon die Dateien meines Minimalbeispiels angehängt:
threadtest_5.ui
main.cpp
threadtest_5.h
threadtest_5.cpp
Worker.h
Worker.cpp
Reichen diese aus?
Ich habe die Auskommentierung der context->start()-Funktion wieder rückgängig gemacht und auch an genau die Stelle, die du mir beschrieben hast, gesetzt. Trotzdem wird die Schleife nicht ausgeführt. Die Änderung habe ich jetzt nicht noch einmal neu hochgeladen, daher ist dies in der threadtest_5.cpp noch nicht so implementiert.
Viele Grüße,
Johannes
Das kommt davon, wenn man das Q_OBJECT in der Klassendefinition vergisst
Worker.h muss dann natürlich auch im .pro unter HEADERS stehen.
Code: Alles auswählen
class Worker :
public QObject
{
Q_OBJECT // WICHTIG WICHTIG WICHTIG :P
public:
Worker(void);
~Worker(void);
public slots:
void doWork();
};
Hach
Vielen Dank!
Manchmal sind es die kleinen Dinge, die einem große Probleme machen.
Wenn ich es richtig verstehe habe ich in der worker-Klasse dann aber keine EventLoop mehr, oder? Müsste ich also einen eventuellen QTimer dann auf dem GUI-Thread laufen lassen? Ich hab das auch schon implementiert und es läuft auch, aber ist das auch die "schönste" Methode?
Des Weiteren kann man anscheinend auch die Funktion nicht einfach per worker->doWork() aufrufen, da sie dann wieder auf dem aufrufendem Thread (GUI-Thread) läuft. Sehe ich das richtig, oder gibt es außer dem Signal - Slot Prinzip noch andere Möglichkeiten eine Funktion, die auf einem anderen Thread mit dieser "richtigen" Methode läuft, aufzurufen?
Vielen Dank nochmal!
Johannes
Vielen Dank!
Manchmal sind es die kleinen Dinge, die einem große Probleme machen.
Wenn ich es richtig verstehe habe ich in der worker-Klasse dann aber keine EventLoop mehr, oder? Müsste ich also einen eventuellen QTimer dann auf dem GUI-Thread laufen lassen? Ich hab das auch schon implementiert und es läuft auch, aber ist das auch die "schönste" Methode?
Des Weiteren kann man anscheinend auch die Funktion nicht einfach per worker->doWork() aufrufen, da sie dann wieder auf dem aufrufendem Thread (GUI-Thread) läuft. Sehe ich das richtig, oder gibt es außer dem Signal - Slot Prinzip noch andere Möglichkeiten eine Funktion, die auf einem anderen Thread mit dieser "richtigen" Methode läuft, aufzurufen?
Vielen Dank nochmal!
Johannes
Sicher hast du eine EventLoop - die im context-Thread. Gestartet wird die mit start() So kann dann auch ein Timer über den neuen Thread abgearbeitet werden.MisterJ hat geschrieben:Wenn ich es richtig verstehe habe ich in der worker-Klasse dann aber keine EventLoop mehr, oder? Müsste ich also einen eventuellen QTimer dann auf dem GUI-Thread laufen lassen?
Klar. Geht aber auch nur mit Qt-Mechanismen:Des Weiteren kann man anscheinend auch die Funktion nicht einfach per worker->doWork() aufrufen, da sie dann wieder auf dem aufrufendem Thread (GUI-Thread) läuft. Sehe ich das richtig, oder gibt es außer dem Signal - Slot Prinzip noch andere Möglichkeiten eine Funktion, die auf einem anderen Thread mit dieser "richtigen" Methode läuft, aufzurufen?
QMetaObject::invokeMethod()
Hallo franzf,
dass man eine EventLoop auf dem context-Thread laufen hat, erschließt sich mir. Das finished()-Signal, das am Ende der überschriebenen run()-Funktion emitiert wurde geht damit aber verloren, oder? Man müsste quasi am Ende einer aufgerufenen Funktion wiederum ein Signal emitieren, dass dieses ersetzt.
In der worker-Klasse habe ich nun in der doWork()-Funktion vor die while(true)-Schleife einen QTimer gesetzt, der einmalig nach 2 Sekunden eine Funktion auslöst. Diese wird aber nicht ausgelöst. Liegt das daran, dass der Thread mit der Schleife beschäftigt ist? Ich dachte, die QTimer würden im Prinzip nochmal in einem eigenen Thread im Hintergrund laufen.
Worker.h
Worker.cpp
Und noch eine Frage zum Schluss:
Wenn eine Funktion aus einem Thread eine Funktion aus der GUI so schnell hintereinander aufruft, dass in der Zwischenzeit die GUI-Funktion nicht beendet werden kann, wird dann eine zweite Instanz dieser Funktion ausgeführt, wartet der Funktionsaufruf, bis die vorherige Funktion beendet wurde und startet dann, oder bekriegen sich beide Funktionsaufrufe?
Viele Grüße
Johannes
dass man eine EventLoop auf dem context-Thread laufen hat, erschließt sich mir. Das finished()-Signal, das am Ende der überschriebenen run()-Funktion emitiert wurde geht damit aber verloren, oder? Man müsste quasi am Ende einer aufgerufenen Funktion wiederum ein Signal emitieren, dass dieses ersetzt.
In der worker-Klasse habe ich nun in der doWork()-Funktion vor die while(true)-Schleife einen QTimer gesetzt, der einmalig nach 2 Sekunden eine Funktion auslöst. Diese wird aber nicht ausgelöst. Liegt das daran, dass der Thread mit der Schleife beschäftigt ist? Ich dachte, die QTimer würden im Prinzip nochmal in einem eigenen Thread im Hintergrund laufen.
Worker.h
Code: Alles auswählen
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QTimer>
class Worker :
public QObject
{
Q_OBJECT
public:
Worker(void);
~Worker(void);
QTimer * timer;
public slots:
void doWork();
void timeoutFunction();
};
#endif
Code: Alles auswählen
#include "Worker.h"
Worker::Worker(void)
{
}
Worker::~Worker(void)
{
}
void Worker::doWork()
{
timer = new QTimer;
QTimer::singleShot(1000, this, SLOT(timeoutFunction()));
while(true);
}
void Worker::timeoutFunction()
{
}
Wenn eine Funktion aus einem Thread eine Funktion aus der GUI so schnell hintereinander aufruft, dass in der Zwischenzeit die GUI-Funktion nicht beendet werden kann, wird dann eine zweite Instanz dieser Funktion ausgeführt, wartet der Funktionsaufruf, bis die vorherige Funktion beendet wurde und startet dann, oder bekriegen sich beide Funktionsaufrufe?
Viele Grüße
Johannes
Es geht nichts verloren.. der Thread emittet dieses Signal noch immer... nur: wenn dein Slot fertig ist läuft der Thread halt noch immer.. daher hast du schon recht: eigenes Signal "arbeitIstFertig".MisterJ hat geschrieben:...Das finished()-Signal, das am Ende der überschriebenen run()-Funktion emitiert wurde geht damit aber verloren, oder? Man müsste quasi am Ende einer aufgerufenen Funktion wiederum ein Signal emitieren, dass dieses ersetzt.
Ja.. genau daran liegt es.MisterJ hat geschrieben: In der worker-Klasse habe ich nun in der doWork()-Funktion vor die while(true)-Schleife einen QTimer gesetzt, der einmalig nach 2 Sekunden eine Funktion auslöst. Diese wird aber nicht ausgelöst. Liegt das daran, dass der Thread mit der Schleife beschäftigt ist?
Falsch gedacht.. die Timer brauchen die Eventloop. Wenn du in der GUI "while (1);" schreibst, gehen die (GUI-)Timer genau so wenig wie wenn du im Thread "while (1);" ausführst..MisterJ hat geschrieben: Ich dachte, die QTimer würden im Prinzip nochmal in einem eigenen Thread im Hintergrund laufen.
Das ist eine komische Frage welche zeigt, dass du Threads noch nicht so ganz verstanden hast... Wenn ein Thread eine Methode aufruft, läuft die halt in dessen Kontext. Du kannst eine Methode nicht "schneller als sie beendet wurde" aufrufen. Allerdings kann die gleiche Methode von zwei unterschiedlichen Kontexten aufgerufen werden (von der GUI und vom Thread gleichzeitig). Was dann geschieht hängt von der Methode ab.MisterJ hat geschrieben: Wenn eine Funktion aus einem Thread eine Funktion aus der GUI so schnell hintereinander aufruft, dass in der Zwischenzeit die GUI-Funktion nicht beendet werden kann, wird dann eine zweite Instanz dieser Funktion ausgeführt, wartet der Funktionsaufruf, bis die vorherige Funktion beendet wurde und startet dann, oder bekriegen sich beide Funktionsaufrufe?
Aber designtechnisch ist das eh falsch: ein Thread darf in der GUI nicht herumfummeln. Er darf ein Signal senden welches die Thread-Resultate gleich mitliefert oder die GUI zu irgendwas bewegt (Statusanzeige ändern, Resultate abholen oder sonst was).
hth..