PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Qt: Non-Threaded Applikation auf saubere Threadstruktur umstellen, wie?



axeljaeger
29-06-2004, 15:13
Hallo,
ich habe eine QtApplikation, die eine Anzahl von kleinen Datenpäckchen (in meinem Fall QImages) verarbeiten muss. Das dauert seine Zeit. Um dem Benutzer das Warten zu versüßen und die Benutzerinteraktion während des Verarbeitens weiter zu ermöglichen, habe ich einen Tipp aus der Qt Doku übernommen:


QTimer *t = new QTimer( myObject );
connect( t, SIGNAL(timeout()), SLOT(processOneThing()));
t->start( 0, FALSE );

Weiter unten heist es aber, dass dieses ein Hack ist und dass man eher Threads verwenden soll. Ich hab das auch mal versucht, den Ansatz aber wieder verworfen, weil das im Hängen der Anwendung geendet hat und das Programm unbedingt zum Linuxtag fertig werden sollte. Ich hab auch keine Idee, wie ich folgendes sinnvoll in Threads verpacke:

- Es ist auf eine Anzahl QImages ein Filter anzuwenden

- Wenn ein QImage fertig ist, soll es angezeigt werden.

- Während die Verarbeitung läuft, soll es möglich sein, den Filter zu verändern. Wenn das passiert ist, soll die aktuelle Bearbeitung der QImages abgebrochen werden und von vorne mit dem neuen Filter angefangen werden. Der Thread muss also Befehle entgegen nehmen können und muss ein Event rausschicken, wenn er mit einem QImage fertig ist.

- Um ein QImage anzuzeigen, muss ich es in eine QPixmap konvertieren. Das dauert auch seine Zeit und darf meines Wissens nach nur im Hauptthread gemacht werden, weil da mit dem Windowsystem seitens Qt gesprochen wird.

Ist es in meiner Situation aus oben genannten Gründen überhaupt sinnvoll, einen Thread einzusetzen?

Mein erster Versuch sah so aus, dass der Thread einen Pointer auf das Array mit den QImages im Hauptthread hatte und dieses dann bearbeitet hat.

Vielen Dank für Doku, Tipps und Ideen.

anda_skoa
30-06-2004, 01:13
Das mit dem Timer ist zwar nicht so elegant, aber solange du damit auskommst, solltest du es vielleicht dabei belassen.

Wenn du Threads benutzt wird es um ein paar Größenordnugen komplizierter, Synchronisation und so.

Ciao,
_

axeljaeger
30-06-2004, 14:06
Klar wird es komplizierter, aber elegant finde ich meine Lösung nicht und ich wüsste gerne, wie man es richtig macht. Außerdem bringt ja multithreading schon etwas auf P4 HT oder Mehrprozessorsystemen.

anda_skoa
30-06-2004, 15:38
Ok, mal ein kleines Konzept



//! Job (Basis-)Klasse
/*! Enthält einen Job Typ und die Daten
*/
class Job
{
};




//! Synchronisierte Übergabestruktur
/*! Die Threads und der Mainthread kommunizieren über die
JobQueue, deren Methoden intern über Locks gesichter sind.
Der Main Thread erzeugt Jobs und gibt sie in die Queue,
die Threads holen sie (bzw werden blockiert wenn keine vorhanden sind)
und geben das Resultat zurück.
*/
class JobQueue
{
public:
virtual Job* getJob() = 0;
virtual void putJob(Job* job) = 0;

virtual Job* getResult() = 0;

virtual void putResult(Job* job) = 0;
};




class Thread : public QThread
{
public:
Thread(JobQueue* queue);
};




//! Verarbeitungsmodul
/*! Spaltet eine Aufgabe in Jobs und füllt die Job Queue.
Fügt die Einzelresultate zum Endergebnis zusammen.
*/
class Processing : publc QObject, public JobQueue
{
Q_OBJECT
public:
virtual Job* getJob();
virtual void putJob(Job* job);

virtual Job* getResult();
virtual void putResult(Job* job);

//! Starts image processing
/*!
\param image image to transform
\param filter filter to apply
\return ID of the transformation Job or -1
*/
int processImage(const QImage& image, Filter filter);

signals:
void processingFinished(int id);
};


Processing::putJob würde dabei Prüfen, ob das Bild fertig ist und dann sich selbst ein Custom Signal senden.
Im Event handler dann das Signal emitten.

ciao,
_

axeljaeger
01-07-2004, 20:32
Ich hab das ganze mal fürs Auge etwas netter als Grafik gemacht, da kann man dann gleich schaun, ob ich es auch richtig verstanden hab. Noch zwei Fragen:
- Wie viele Instanzen von Thread gibt es? Ich dachte an eine, die ein Image nach dem anderen abarbeiten kann.
- Kann Processing irgendwie der Application Bescheid geben, wenn ein Job fertig geworden ist? Wenn ich nach putJob ein Signal sende, befürchte ich, passieren böse Dinge.

anda_skoa
01-07-2004, 20:37
Ich hab das ganze mal fürs Auge etwas netter als Grafik gemacht, da kann man dann gleich schaun, ob ich es auch richtig verstanden hab. Noch zwei Fragen:
- Wie viele Instanzen von Thread gibt es? Ich dachte an eine, die ein Image nach dem anderen abarbeiten kann.

zum Beispiel einen



- Kann Processing irgendwie der Application Bescheid geben, wenn ein Job fertig geworden ist? Wenn ich nach putJob ein Signal sende, befürchte ich, passieren böse Dinge.

Darum muss putResult auch ein Event an Processing schicken und im Eventhandler dann das Signal emitten, sonst wird der verbundene Slot im Kontext des Threads aufgerufen.

Ciao,
_

axeljaeger
02-07-2004, 14:05
Jetzt bliebe noch zu klären, wie man die aktuelle Bearbeitung canceln kann. Intern wird ja Processing eine Liste mit Jobs halten. Die einfach leermachen?

anda_skoa
02-07-2004, 14:24
Leeren der Job Queue ist schon mal gut.

Du kannst auch in deiner Threadklasse eine Methode und ein Flag einbauen, die die aktuelle Bearbeitung abbrechen.

zB


class Thread : public QThread
{
public:
void cancel() { m_cancel = true; }

private:
bool m_cancel;
};


Der Thread wird ja wahrscheinlich eine Art Schleife über das Image haben und kann einfach in jedem Durchgang das Flag prüfen und bei true gleich wieder getJob() aufrufen.

Man kann das Flag noch synchronisieren, wenn man im Aufrufer wissen will, wann der Thread cancelled, aber da es mehr für die schneller Rückgewinnung des Threads ist, braucht man es IMHO nicht unbedingt.

Man könnte auch eine Art Zwischenstufe machen, also das threadinterne Flags als eine Art Hinweis, doch mal eine synchronisierte Methode in der JobQueue aufzurufen.

Bei einem Thread reicht aber IMHO das Flag allein.

Ciao,
_