QThread kurzzeitig stoppen

Alles rund um die Programmierung mit Qt
Antworten
mase
Beiträge: 39
Registriert: 8. Mai 2007 00:17

QThread kurzzeitig stoppen

Beitrag von mase »

Hallo!
Ich habe in einem QThread folgende run() implementiert:

Code: Alles auswählen

void cMeasureThread::run()
{
    while (!m_StopThread || !m_Loop)
    {
        StartMeasurement();
    }

    return;
}
m_StopThread wird von der GUI-Klasse auf true gesetzt, wenn der
Thread gestoppt werden soll. Auch wenn m_Loop false wird, soll
der Thread stoppen.
Das Stoppen funktioniert auch, aber wie krieg ich ihn wieder gestartet?
Ein erneuter Aufruf von start() bewirkt gar nichts.
Ich würde mich schon mit Pausieren zufrieden geben.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

Die Thread-Doku von Qt ist im übrigen auch nicht so übel... QWaitCondition wäre eine Möglichkeit...
MiKla
Beiträge: 134
Registriert: 29. Juli 2007 15:29

Beitrag von MiKla »

Wenn beide Bits "m_StopThread, m_Loop" 0 sind, sollte der Thread wieder starten. Sind sie das (Debugger)? Außer der Thread/Objekt wurde zerstört!

Michael
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag von Superheftig »

Code: Alles auswählen

void cMeasureThread::run()
{
    while (!m_Loop)
    {
        while(!m_StopThread) {
             msleep(10);
        }

        StartMeasurement();
    }

    return;
}
So legt sich der Thread schlafen sobald du Stop Thread auf false setzt und versucht alle 10 msecs wieder zu starten.
Falls du StopThread wieder auf true setzt gehts also spätestens nach 10 msecs wieder weiter.
mase
Beiträge: 39
Registriert: 8. Mai 2007 00:17

Beitrag von mase »

Superheftig hat geschrieben:

Code: Alles auswählen

void cMeasureThread::run()
{
    while (!m_Loop)
    {
        while(!m_StopThread) {
             msleep(10);
        }

        StartMeasurement();
    }

    return;
}
So legt sich der Thread schlafen sobald du Stop Thread auf false setzt und versucht alle 10 msecs wieder zu starten.
Falls du StopThread wieder auf true setzt gehts also spätestens nach 10 msecs wieder weiter.
Das funktioniert fast. Wenn jedoch m_Loop einmal true wurde, lässt sich
der Thread nicht mehr starten. Auf m_StopThead wird korrekt reagiert.
Vielleicht die ganze Funktion in eine Forever-Schleife legen, und weitere
10ms für m_Loop einfügen.
Ich hab's jetzt mal so probiert:

Code: Alles auswählen

void cMeasureThread::run()
{
    while (true)
    {
        if (m_Continue)
        {
            StartMeasurement();

            if (!m_Loop)
            {
                m_Continue = false;
            }

        }
        else
        {
            sleep(1);
        }

    }

    return;
}
m_Loop steht auf true, wenn die Messung in einer Schleife ablaufen soll.
m_Continue wird von aussen auf false gesetzt, wenn der Thread
pausiert werden soll. Das sleep(1) war notwendig, weil der GUI-Thread
sonst keinen Einfluss nehmen kann. Jedoch durchläuft der Thread
beim Pausieren ständig die Forever-Schleife und fragt m_Continue ab.
Nicht gerade elegant.
Ich hab auch noch ein kleines Timing-Problem. Nachdem die GUI den
Thread pausiert hat, sollen die Messwerte auf 0 gesetzt werden. Das
Geschieht im GUI-Thread selber. Allerdings kann es vorkommen, dass
nach dem Setzen auf 0 gerade noch die letzten Messwerte auf dem
Messthread kommen. Die GUI aktualisiert die Messwerte natürlich
in ihren Anzeigen. Selbst wenn ich nach dem Setzen von m_Continue
durch die GUI noch 2 Sekunden warte, ist das Setzen auf 0 nicht
zuverlässig.

Mit den WaitConditions und Mutex hab ich stundenlang rumexperimen-
tiert. Nichts hat richtig funktioniert. Der GUI-Thread muss den Mess-
thread sperren, und auch wieder freigeben können.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

C++-Buch , suche nach 'volatile' sollte hier helfen.
Um es kurz zu erklären - der Compiler darf (und wird auch) optimieren und da sich für ihn der Wert von m_Continue bzw. m_Loop nicht ändert wird der Zugriff wegoptimiert.
Ein einfacher QMutex sollte aber genügen, oder eben volatile wobei dann eine Race-Condition auftreten kann wenn beide Threads gleichzeitig schreibend z.B. auf m_Continue zugreifen.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag von Superheftig »

K Hier mit QWaitCondition

Code: Alles auswählen

QMutex *mutex;
QWaitCondition *waitCondition;
bool stop;
bool hasStopped;
Die variablen definierst du als member der Klasse cMeasureThread und beim erstellen der Klasse übergibst du die waitCondition dem Thread als Pointer. Damit haben beide Zugriff auf die selber variable

Code: Alles auswählen

cMeasureThread(QMutex *m; QWaitCondition *w) 
  : mutex(new QMutex), waitCondition(w), stop(false), hasStopped(false) {}
Die run Methode sieht dann so aus:

Code: Alles auswählen

void cMeasureThread::run() {
    while (true)  {
       if (stop) {
          mutex.lock()
          hasStopped = true;
          waitConditon.wait(mutex);
          stop = false;
          hasStopped = false;
          mutex.unlock();
       }
       StartMeasurement(); 
    }
}
Zum stoppen machst du folgende Methode:

Code: Alles auswählen

void cMeasureThread::stop() {
  QMutexLocker locker(mutex);
  stop = true;
}
Um aus dem Hauptthread zu testen ob der cMeasureThread fertig ist nimmst du die funktion. Dann weißt du wann keine neuen werte mehr kommen:

Code: Alles auswählen

bool cMeasureThread::hasFinished() {
  QMutexLocker locker(mutex);
  return hasStopped;
}

Der Hauptthread der in aufwecken soll kriegt folgende Methode:

Code: Alles auswählen

void HauptThread::startMeasurement() {
   waitCondition.wakeAll();
}
mase
Beiträge: 39
Registriert: 8. Mai 2007 00:17

Beitrag von mase »

Wem soll ich denn die WaitCondition übergeben? cMeasureThread ist
doch mein Thread. Dann muss doch mene GUI die WaitCondition
erstellen.
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag von Superheftig »

Wo du die erstellst ist völlig egal. Es muss nur der Thread der gestopt werden soll und der Thread der Stoppen bzw starten soll zugriff auf das gleiche QWaitCondition Objekt haben. Kannst es auch im cMeasureThread erstellen und dann dem anderen Thread mit ner get Methode geben.
mase
Beiträge: 39
Registriert: 8. Mai 2007 00:17

Beitrag von mase »

Jetzt hab ich's kapiert! Ich hab's jetzt so gelöst, dass ich die beiden
Objekte im GUI-Thread erstelle, und die beiden Zeiger an den
Mess-Thread übergebe. Die Variablennamen hab ich ihrer Funktion
angepasst:

Code: Alles auswählen

void cMeasureThread::run()
{
    while (true)
    {
        if (!m_ThreadEnabled)
        {
            emit MeasurementDisabled();
            m_WaitCondition->wait(m_Mutex);
        }

        StartMeasurement();

        if (!m_Loop)
        {
            m_WaitCondition->wait(m_Mutex);
        }

    }

    return;
}
Ich hatte vorher vor dem wait() den Mutex gesperrt. Dann konnte der
Thread aber nicht mehr aufgeweckt werden. Warum weiss ich nicht,
obwohl es so im QT4-Buch steht. Das emit teilt der GUI mit, dass der
Thread beendet wurde.
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Hab nicht alles durchgelesen, aber wäre das nicht eine Alternative:

Code: Alles auswählen


void Thread::run()
{
   mutex.lock();

   while(isRunning())
   {
      while (!doMeasurement)
         waitCond.wait(mutex);

      startMeasurement(); 
   }

   mutex.unlock();
}

void Thread::wakeUp()
{
   doMeasurement = 1;
   waitCond.wakeOne();
}

void Thread::startMeasurement()
{
   ...
   doMeasurement = 0;
}
Wobei ich mir nicht sicher bin, ob Thread::exit()/::quit das running auf false setzt, und den Thread somit beendet :)
Antworten