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]
}
}
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();
}
Code: Alles auswählen
forever
{
// [1]
mutex.lock();
keyPressed.wait(&mutex);
++count;
mutex.unlock();
do_something();
mutex.lock();
--count;
mutex.unlock();
// [1]
}
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;
}
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?