QThreads und static-Objekte

Alles rund um die Programmierung mit Qt
Antworten
AlGaN
Beiträge: 25
Registriert: 25. März 2006 12:45

QThreads und static-Objekte

Beitrag von AlGaN »

Hallo,

ich habe vermutlich ein Problem mit o.g. Objekten. Folgendes Szenario: In meiner App-Klasse installiere ich einen Message-Handler für Debug-Messages, dessen Funktion eine Funktion in einem statischen Klassen-Member aufruft:

App.hpp:

Code: Alles auswählen

class App : public QApplication
{
Q_OBJECT

public:
   App( int argc, char* argv[]);
  ~App();

   static Log::LogMessage log( Log::LogLevel, QString msg );

[...]
private:
    static void qt_msg_handler( QtMsgType type, const char* msg);
    static Log mLog;
    Engine* mEngine;
[...]
};
In der Implementierungsdatei definiere ich dann das statische Log-Objekt und die Funktion für den Message-Handler:

Code: Alles auswählen

[...]
/* static member variables */
Log App::mLog;

void App::qt_msg_handler( QtMsgType type, const char* s)
{
    QString msg(s);
    switch (type)
    {
     case QtDebugMsg:
          mLog.log( "QtDebugMsg: %1").arg( msg );
          break;
     [...]
}
Im Konstruktor von App setze ich den MsgHandler:

Code: Alles auswählen

App:App(int argc, char* argv[])
{
    // install qt debug handler
    qInstallMsgHandler( qt_msg_handler );
    [...]
    // construct Engine thread
    mEngine = new Engine( this );
    [...]
    mEngine->start();
}
Soweit so gut. In der App werden aber noch jede Menge andere Objekte initialisiert und ans Laufen gebracht, u.a. mehrere von QThread abgeleitete Objekte, die bis zum Beenden des Programms durchlaufen:

Code: Alles auswählen

class Engine: public QThread
{
     Q_OBJECT
public:
    Engine( QObject *parent );

    ~Engine();

protected:
    virtual void run();
    
    virtual void stop();
[...]
};
Engine.cpp

Code: Alles auswählen

Engine::Engine( QObject* parent )
    : QThread( parent )
{
    [...]
    start();
}

Engine::~Engine()
{
    qDebug() << "Stopping Engine"; // <-- Fehler ??
    stop();
    [...]
}

void Engine::stop()
{
    mRunningLock.lock();
    mRunning = false;
    mRunning.unlock();

    if ( !wait( 50 ) )
    {
        qDebug() << "Thread hangs! this should never happen!";
    }
}
Soweit so gut. Wenn das Programm beendet wird, wird der Destruktor der App aufgerufen und der Thread beendet:

Code: Alles auswählen

App:~App()
{
    mEngine->stop();
    [...]
}
Das Problem ist, dass wenn die App sich beendet der Thread ja noch laufen kann, bis er sich beendet, wird ja mit wait() vorgegeben, d.h. er wartet 50 ms darauf, das dir run()-Methode terminiert. Leider bekomme ich eine Exception, wenn sich das Programm beendet, und ich vermute, dass sich das statische mLog-Objekt schon zerstört hat, während der Thread immer noch läuft. Meines Wissens werden statische Objekte zerstört, nachdem sich main() beendet hat.

Deshalb meine Frage: Können QThreads noch laufen, wenn sich main() (im Hauptthread) beendet hat?
Wenn ja: Wie kann man das verhindern ?

Nachtrag: Ein Test mit qDebug()'s am Ende des App::~App Destruktors zeigt, dass danach noch jede Menge Debug-Meldungen aus den Thread(s) kommen, d.h. sie laufen wohl auch noch, wenn schon das QApp-Objekt zerstört wurde.

Bin für alle Tipps dankbar,
AlGaN
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Naja, mit deinen Fragen gibst Dir eigentlich schon selber die antworten ...
Deshalb meine Frage: Können QThreads noch laufen, wenn sich main() (im Hauptthread) beendet hat?
Klar, und wenn man es ned verhindert tun sie das sogar ....
Was eigentlich immer ne ganze Menge Probleme mit sich bringt.
Dem BS ist es eigentlich wurscht, dem koennte der mainthread nur paar andere threads erzeugen und sich dann beenden, dem wuerde das nix ausmachen. Der macht da keine Unnerschiede.
Problem an deiner stelle sind die startuproutinen und exitHandler der runtime, die vom mainthread getriggert werden und unter anderem deine globalen und pseudoglobalen(statischen) variablen initialisieren und destruieren.
Man kann das auch verbiegen, aber da begiebt man sich auf sehr compilerspezifische pfade ...
Wenn ja: Wie kann man das verhindern ?
Der einfachere Weg ist den Mainthread direkt oder indirekt auf die beendigung deines threades warten zu lassen. (wichtig: richtig verlassen das der thread auch tot ist kann man nur mit den speziellen dafuer vorgesehenen funktionen. Bei der Qt kapselt QThread::wait das fuer dich)

wichtig zu wissen ist, das die reihenfolge in der statische und globale objekte initialisiert und deinitialisiert werden, eigentlich unbestimmt ist, mann kann sich da auf nix verlassen.
Aber alle diese Objecte erst destruiert werden nachdem der scope der main verlassen wird.

So dass man das QThread::wait auf den Qthread entweder in der main, oder im destruktur eines objectes was im scope der main liegt, vornehmen kann.
oder klar in irgend einer methode irgendwo, hauptsache nur sie wird auch aufgerufen.

Und wie bei allen hier der hinweis, sich mal mit Threads nativ auseinandersetzen (winapi oder pthreads). die Qt vereinfacht zwar vieles und schoen, und ich nutz die auch gerne, aber irgendwie verteckt sie die Grundlagen hinter was maechtigen was eine einfacheres handling vorgaukelt als wie es in wirklichkeit ist.

Ciao ...
AlGaN
Beiträge: 25
Registriert: 25. März 2006 12:45

Beitrag von AlGaN »

Hallo RHBaum,

danke für Deine Antwort, mir ist bei längerem Nachdenken auch der Gedanke gekommen, dass das Beenden vom Threads wohl in diesem Fall die Ursache ist...

Ich hab mich inzwischen auch ein wenig in die Grundlagen von pthreads eingelesen, und dort wird ja im Prinzip auch im Main-Thread mit pthread_join() auf das Ende der anderen laufenden Threads gewartet.
Die Winapi-Version wird wohl ähnlich aufgebaut sein...
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Die Winapi-Version wird wohl ähnlich aufgebaut sein...
du kannstd avon ausgehen, das die funktionen das aequivalent sind.
Sie heissen bissi ander und entscheiden sich in paar winzigen details.

Problem bei threads ist eher das verstaendniss. Also wenn mit pthreads umgehen kannst, wird dir die winapi keine probleme machen.
Ausserdem glaub ich gibts ne pthreads kompatible lib, die dir die pthread funktionen auf die winapi mappt, so das fuer pthread geschriebener code auch unter windows z.b. kompilierbar und funktional waer.

Ciao ...
Antworten