QWaitCondition

Alles rund um die Programmierung mit Qt
Antworten
Jordy
Beiträge: 22
Registriert: 15. August 2005 17:22

QWaitCondition

Beitrag von Jordy »

Hi!

Ich bin gerade mit Multi-Threading beschäftigt. Dazu sollen Threads synchronisiert werden. Ein Sender und mehrere Empfänger.

Prinzipiell soll der Sender irgendwelche Daten in eine gemeinsame Variable schreiben und die Empfänger per QWaitCondition aufwecken.
Die Empfänger sollen, einmal aufgeweckt, die Daten lesen und irgendwas kluges damit anstellen.

So hätte ich mir das prinzipiell vorgestellt:

Code: Alles auswählen

QWaitcondition cond;
int data;
QMutex dataMutex;

void Sender::sendData(int x)
{
   dataMutex.lock();
   data = x;
   dataMutex.unlock();

   cond.wakeAll();
}

void Receiver::run()
{
   while(true)
   {
// [2]

      cond.wait();

// [1]

      dataMutex.lock();
      qDebug() << "daten erhalten: " << data;
      dataMutex.unlock();

// [2]

   }
}
Mir ist klar geworden, daß obiges problematisch ist, wenn sendData() zu kurz nacheinander aufgerufen wird, also z.B. während ein Empfänger gerade wait() verlassen hat, aber den Mutex noch nicht reserviert hat [1] ein weiteres, komplettes sendData() mit wakeAll() ausgeführt wird. Dann würde ein Datum verloren gehen.

Ein weiteres Problem stellt sich, wenn der Sender Daten absetzt, während ein Empfänger den Mutex bereits freigegeben hat, aber noch nicht im wait() wartet [2]. Dann würde ein wakeAll() verloren gehen (und damit auch ein Datum), denn eine QWaitCondition weckt ja nur Threads, die im im entsprechenden Augenblick tatsächlich warten.

Das Beispiel in der Qt Doku hilft mir diesbezüglich nicht weiter (abgesehen davon, daß mir sleep() hier nicht sehr schon erscheint):

Sender:

Code: Alles auswählen

    forever
   {
        getchar();

        mutex.lock();
        // Sleep until there are no busy worker threads
        while (count > 0)
       {
            mutex.unlock();
            sleep(1);
            mutex.lock();
        }
        mutex.unlock();
        keyPressed.wakeAll();
    }
Empfänger:

Code: Alles auswählen

    forever
   {
// [1]

        mutex.lock();
        keyPressed.wait(&mutex);
        ++count;
        mutex.unlock();

        do_something();

        mutex.lock();
        --count;
        mutex.unlock();

// [1]
    }
Auch hier könnte doch der Sender das nächste Ereignis mitteilen, nachdem der letzte Empfänger-Thread "count" dekrementiert hat, aber noch in in wait() auf das Ereignis wartet [1]. Auch hier würde ein Ereignis verloren gehen.

Außerdem könnte (wenn man sich die interne Implementation von wait ansieht) ein wakeAll() verloren gehen, wenn der Sender bereits das nächste Ereignis mitteilt, während der Empfänger (innerhalb von wait()) aufgeweckt wurde, den Mutex aber noch nicht erhalten konnte [2].

Hier ist die gekürzte Windows-Implementierung:

Code: Alles auswählen

bool QWaitConditionPrivate::wait(QMutex *mutex, unsigned long time)
{
    bool ret = false;

    mtx.lock();
    ..... // insert 'wce' into the queue (sorted by priority)
    mtx.unlock();

    mutex->unlock();

    // wait for the event
    switch (WaitForSingleObject(wce->event, time))
    {

// [2]

    default: break;

    case WAIT_OBJECT_0:
        ret = true;
        break;
    }

// [2]

    mutex->lock();

    mtx.lock();
    .....     // remove 'wce' from the queue
    mtx.unlock();

    return ret;
}
Wie also kann dieses Problem gelöst werden, unter der Garantie, daß keinerlei Ereignisse verloren gehen können (auch nicht mit sehr geringer Wahrscheinlichkeit)?

Ich vermute, daß ich hier irgendetwas falsch verstehe, ich kann aber meine obige Argumentation nicht widerlegen und mir scheint, daß die beschrieben Fehler passieren können.

Sind die mit eckigen Klammern gekennzeichneten Codepositionen wie geschildert anfällig? Wenn nicht, warum? Wo ist mein Blackout?

Ich muß im Übrigen gestehen, daß mir (wenn ich das Trolltech-Beispiel und die interne wait() Implemenation so ansehe) ich keine Ahnung habe, warum die wait() methode überhaupt ein mutex als Parameter erwartet und welche Probleme das löst. Ich habe auch bemerkt, daß die Linux-Implementierung völlig anders aussieht, dort wird der Mutex an den kernel weitergereicht. Liegt hier vielleicht der Hund begraben?
uhu01
Beiträge: 150
Registriert: 10. Juli 2005 20:46

Beitrag von uhu01 »

Hy!

Wenn ich mir dein Problem so ansehe, stellt sich die Frage ob für deinen Zweck eine Semaphore nicht eher geeignet ist als eine Wait-Condition, damit können keine Ereignisse verloren gehen, da die Semaphore mitzählt wie oft sie aktiviert wurde. In einer entsprechenden Anzahl löst sie dann auch aus. Ich benutze zumindest meistens Semaphores wenn ich Aufgaben in der Art <Thread soll eine Liste mit aufgaben abarbeiten die in die GUI eingegeben werden>

mfg
uhu01
Jordy
Beiträge: 22
Registriert: 15. August 2005 17:22

Beitrag von Jordy »

uhu01 hat geschrieben:Hy!
Wenn ich mir dein Problem so ansehe, stellt sich die Frage ob für deinen Zweck eine Semaphore nicht eher geeignet ist als eine Wait-Condition, damit können keine Ereignisse verloren gehen, da die Semaphore mitzählt wie oft sie aktiviert wurde. In einer entsprechenden Anzahl löst sie dann auch aus. Ich benutze zumindest meistens Semaphores wenn ich Aufgaben in der Art <Thread soll eine Liste mit aufgaben abarbeiten die in die GUI eingegeben werden>
Naja, gut. Vielleicht keine so schlechte Idee.

Trotzdem wäre es mir wichtig Obiges verstanden zu haben. Wenn ich das Ganze wider erwarten richtig verstanden habe, würde das nämlich bedeuten die QWaitCondition taugt nichts (zumindest nicht für die Beispielanwendung und/oder in der Windowsimplementierung), was mir schwer fällt zu glauben. Mir geht es um das eigene Verständnis und um die Korrektheit von Qt.

Außerdem sieht man solche Lösungsansätze auch unter Verwendung der darunterliegenden Linux-Funktionen (ohne Qt). Der Mechanismus scheint also eine wichtige Rolle zu spielen. Noch ein Grund ihn zu verstehen.

Kannst du das von mir vermutete Verhalten irgendwie argumentativ bestätigen/widerlegen?
Jordy
Beiträge: 22
Registriert: 15. August 2005 17:22

Beitrag von Jordy »

Sonst keine Antworten? Ist das Thema zu banal oder zu exotisch?!
Ich bräuchte wirklich jemand der mir da ein paar Tips geben kann.
macman
Beiträge: 1738
Registriert: 15. Juni 2005 13:33
Wohnort: Gütersloh
Kontaktdaten:

Beitrag von macman »

Jordy hat geschrieben:Sonst keine Antworten? Ist das Thema zu banal oder zu exotisch?!
Wenn es nur ums Verstehen gaht, schau mal hier:
http://www.netzmafia.de/skripten/bs/bs2.html#2.5

Bisher hatte ich noch nicht mit derart zeitkritischen Situationen zu tun, kann also nichts weiter dazu sagen.
uhu01
Beiträge: 150
Registriert: 10. Juli 2005 20:46

Beitrag von uhu01 »

Hy!

Die Probleme die du oben geschildert hast könnten so in der Praxis auftreten, also kein Blackout.

Eine WaitCondition ist in deinem Fall wie oben schon gesagt nicht die richtige Wahl, das Verhalten könnte allerding unter gewissen Umständen gewünscht sein. Der mutex ist notwendig um zu verhindern das zwei Threads gleichzeitig die Daten der QWaitCondition ändern wollen, und diese in einem unvorhersehbaren Zustand hinterlassen.

mfg
uhu01
Jordy
Beiträge: 22
Registriert: 15. August 2005 17:22

Beitrag von Jordy »

uhu01 hat geschrieben:Die Probleme die du oben geschildert hast könnten so in der Praxis auftreten, also kein Blackout.
Das ist ein Wort. Ich hatte schon an mir gezweifelt.
uhu01 hat geschrieben:Eine WaitCondition ist in deinem Fall wie oben schon gesagt nicht die richtige Wahl, das Verhalten könnte allerding unter gewissen Umständen gewünscht sein.
Dann ist das Qt-Doku-Beispiel aber denkbar ungeeignet, oder? Ich meine, wer will schon Tastendrücke verlieren.

Abgesehen davon bin ich mir ziemlich sicher, daß Trolltech sich nicht darüber im Klaren sind, daß ihr Beispiel-Code Ereignisse verlieren kann.
uhu01 hat geschrieben:Der mutex ist notwendig um zu verhindern das zwei Threads gleichzeitig die Daten der QWaitCondition ändern wollen, und diese in einem unvorhersehbaren Zustand hinterlassen.
Damit wären wir beim zweiten wichtigen Punkt: Wenn du dir die interne implementierung von wait() ansiehst (siehe original Posting), dann ist da doch bereits ein Mutex namens "mtx", der die wait() Internas schützt (also die Queue usw.).
Wozu dann noch der übergebene Mutex? ich weiß, daß in der Linux-Implementierung der Mutex an die darunterliegende kernel-Funktion übergeben wird und dort der interne Mutex mtx nicht existiert. Kann es sein, daß in der Windows-Implementierung der übergebene Mutex überflüssig ist (wegen des bereits vorhandenen "mtx" Mutex) und nur wegen der Schnittstellen-kompatibilität übergeben werden muß?
Oder hat der Mutex (außer dem unterstellten Schutz der Internas unter Linux) vielleicht noch eine andere Anwendung (das würde mich sehr interessieren)?
uhu01
Beiträge: 150
Registriert: 10. Juli 2005 20:46

Beitrag von uhu01 »

Hy!
jordy hat geschrieben:Abgesehen davon bin ich mir ziemlich sicher, daß Trolltech sich nicht darüber im Klaren sind, daß ihr Beispiel-Code Ereignisse verlieren kann.
Das denke ich nicht:
QWaitCondition-Doku hat geschrieben: Also, if some of the threads are still in do_something() when the key is pressed, they won't be woken up (since they're not waiting on the condition variable) and so the task will not be performed for that key press.
Die nicht notwendige Übergabe des Mutex wird warsch. so sein wie du gesagt hast, müsste mir den Source mal anschauen.

mfg
uhu01
Jordy
Beiträge: 22
Registriert: 15. August 2005 17:22

Beitrag von Jordy »

uhu01 hat geschrieben:
jordy hat geschrieben:Abgesehen davon bin ich mir ziemlich sicher, daß Trolltech sich nicht darüber im Klaren sind, daß ihr Beispiel-Code Ereignisse verlieren kann.
Das denke ich nicht:
QWaitCondition-Doku hat geschrieben: Also, if some of the threads are still in do_something() when the key is pressed, they won't be woken up (since they're not waiting on the condition variable) and so the task will not be performed for that key press.
Klar. Was Trolltech nicht erwähnen ist die Tatsache, daß ihr spezielles Beispiel sogar Ereignisse verlieren kann, die den Emfpänger bereits erfolgreich aufgeweckt haben.
Wobei ich zugeben muß, daß das im Prinzip das selbe Problem ist, wenn man es einmal verstanden hat.
uhu01 hat geschrieben:Die nicht notwendige Übergabe des Mutex wird warsch. so sein wie du gesagt hast, müsste mir den Source mal anschauen.
Wäre großartig wenn du mal reinschauen könntest. Ich wüßte sehr gerne, ob du zum selben Ergebnis kommst.
Antworten