[gelöst] Thread-Synchronisation
[gelöst] Thread-Synchronisation
Hi Leute,
Nutze Qt 4.6.1 mit aktuellem Creator auf WinXP.
Ich möchte gerade für eine Simulationssoftware die Visualisierung umsetzen. Nun macht man das ja meistens so, dass die Berechnung nach Möglichkeit in einem eigenen Thread ablaufen. Die Darstellung sollte dann möglichste live in der GUI im Hauptthread stattfinden. Weiterhin möchte ich aus der GUI heraus die Parameter (zum Beispiel Zielwerte) der Simulation live ändern können.
Dabei sollten GUI und Rechenthread auf die selben Daten zurück greifen können (über Pointer), da ein Kopieren der Daten aufgrund der Menge unnötigen Overhead mit sich führen würde.
Wer hat eine Idee für ein Konzept der Synchronisierung, dass ich aus meiner GUI heraus Parameter beeinflussen kann, dann die Simulation durchgeführt wird und anschließend die Simulationsergebnisse in der GUI dargestellt werden, und zwar so, dass Daten auch wirklich konsistent sind?
Habe es schon mit QMutexen und QWaitConditions probiert, aber noch nichts vollständig funktionsfähiges hin bekommen.
Man muss ja dabei beachten, dass die Mutexe im selben Thread freigegeben werden aus dem sie angefordert wurden...
Unterstützt Qt noch weitere Synchronisationsmechanismen (Semaphoren sind ja prinzipiell das selbe wie Mutexe) zum Beispiel sowas wie Events? (wie bei VxWorks)
Mit Signals und Slots kann man ja leider auch nicht arbeiten, da deren Ausführungszeitpunkt nicht genau bestimmt ist.
Grüße,
Kay
Nutze Qt 4.6.1 mit aktuellem Creator auf WinXP.
Ich möchte gerade für eine Simulationssoftware die Visualisierung umsetzen. Nun macht man das ja meistens so, dass die Berechnung nach Möglichkeit in einem eigenen Thread ablaufen. Die Darstellung sollte dann möglichste live in der GUI im Hauptthread stattfinden. Weiterhin möchte ich aus der GUI heraus die Parameter (zum Beispiel Zielwerte) der Simulation live ändern können.
Dabei sollten GUI und Rechenthread auf die selben Daten zurück greifen können (über Pointer), da ein Kopieren der Daten aufgrund der Menge unnötigen Overhead mit sich führen würde.
Wer hat eine Idee für ein Konzept der Synchronisierung, dass ich aus meiner GUI heraus Parameter beeinflussen kann, dann die Simulation durchgeführt wird und anschließend die Simulationsergebnisse in der GUI dargestellt werden, und zwar so, dass Daten auch wirklich konsistent sind?
Habe es schon mit QMutexen und QWaitConditions probiert, aber noch nichts vollständig funktionsfähiges hin bekommen.
Man muss ja dabei beachten, dass die Mutexe im selben Thread freigegeben werden aus dem sie angefordert wurden...
Unterstützt Qt noch weitere Synchronisationsmechanismen (Semaphoren sind ja prinzipiell das selbe wie Mutexe) zum Beispiel sowas wie Events? (wie bei VxWorks)
Mit Signals und Slots kann man ja leider auch nicht arbeiten, da deren Ausführungszeitpunkt nicht genau bestimmt ist.
Grüße,
Kay
Zuletzt geändert von Kay am 6. April 2010 16:20, insgesamt 1-mal geändert.
Re: Thread-Synchronisation
Sowas wie Events -> Eventloop / Signals & SlotsKay hat geschrieben:Hi Leute,
Nutze Qt 4.6.1 mit aktuellem Creator auf WinXP.
Ich möchte gerade für eine Simulationssoftware die Visualisierung umsetzen. Nun macht man das ja meistens so, dass die Berechnung nach Möglichkeit in einem eigenen Thread ablaufen. Die Darstellung sollte dann möglichste live in der GUI im Hauptthread stattfinden. Weiterhin möchte ich aus der GUI heraus die Parameter (zum Beispiel Zielwerte) der Simulation live ändern können.
Dabei sollten GUI und Rechenthread auf die selben Daten zurück greifen können (über Pointer), da ein Kopieren der Daten aufgrund der Menge unnötigen Overhead mit sich führen würde.
Wer hat eine Idee für ein Konzept der Synchronisierung, dass ich aus meiner GUI heraus Parameter beeinflussen kann, dann die Simulation durchgeführt wird und anschließend die Simulationsergebnisse in der GUI dargestellt werden, und zwar so, dass Daten auch wirklich konsistent sind?
Habe es schon mit QMutexen und QWaitConditions probiert, aber noch nichts vollständig funktionsfähiges hin bekommen.
Man muss ja dabei beachten, dass die Mutexe im selben Thread freigegeben werden aus dem sie angefordert wurden...
Unterstützt Qt noch weitere Synchronisationsmechanismen (Semaphoren sind ja prinzipiell das selbe wie Mutexe) zum Beispiel sowas wie Events? (wie bei VxWorks)
Mit Signals und Slots kann man ja leider auch nicht arbeiten, da deren Ausführungszeitpunkt nicht genau bestimmt ist.
Grüße,
Kay
Was ist das Problem mit dem Mutex?
Code: Alles auswählen
class MyThread
{
public:
void setParameter(void* arg)
{
QMutexLocker m(&mutex);
someMember= arg;
}
protected:
int run()
{
mutex.lock();
doSomethingWith(someMember);
mutex.unlock();
}
QMutex mutex;
void* someMember;
};
Soll heißen: Auch wenn du vollkommen threadsafe hier die Variable someMember änderst, könntest du das Objekt trotzdem noch problemlos vom Mainthread aus ändern, da du hier ja nur einen Pointer hast.
Damit wäre zwar der gleichzeitige Zugriff auf die gemeinsame Ressource verhindert, aber ich kann nicht stuern, ob die GUI nach oder vor der Simulation aktualisiert wird. Im Simulationsthread gibt es nämlich nur eine Stelle, wo alle Daten aktuell sind. Diese sollten dann auch nur in diesem Moment angezeigt werden. Dabei muss die GUI aber halt jederzeit ansprechbar bleiben.
Naja, die Events in VxWorks waren mehr so eine Art Flag auf die man halt warten konnte ohne CPU-Power zu verschwenden und die man beliebig setzen und zurücksetzen konnte (threadunabhängig!).
Zur Zeit ist es so, dass die Simulation auch nicht laufen brauch, wenn ich an der GUI keine neuen Daten per Slider einstelle. Aktuell ist es so gedacht, dass ich in dem Slot für die Slider die neuen Parameter aus den Slidern ermittle, dann die Kontrolle an den Simulationsthread abgeben möchte und wenn dieser fertig simuliert hat, noch im Slot die neuen Daten anzeigen möchte.
Eine Datenanzeigefunktion in der GUI wäre nicht möglich, da der Aufruf dieser Funktion aus dem Simulationsthread heraus zum Programmabsturz führen würde (Zugriff auf GUI-Funktionen aus Nebenthreads sind nicht möglich).
Ein Signal aus der Simulation heraus zu senden ist auch doof, da ich nicht weiß, wann der Slot ausgeführt wird und ob dann die Daten aktuell sind oder nicht.
Ich müsste also aus meiner GUI heraus die neue Berechnung anstoßen können (weiterer Schleifendurchlauf in der Threadschleife) und dann aus dem Simulationsthread heraus die Darstellung anstoßen können und das immer im richtigen Moment.
Das Problem ist, dass es später auch möglich sein soll, dass jemand anderes eigene GUIs und Simulationsmodelle per Plugin einbinden könnte. Dann kann es sein, dass vielleicht gar keine GUI gebraucht wird, oder aber dass die GUI nur die Darstellung übernimmt oder nur die Steuerung.
Sonst könnte ich die Berechnung ja gleich in dem Slot für die Slider machen. Aber das wäre wieder nicht möglich, wenn die GUI später nur die Darstellung übernehmen soll (zum Beispiel nur ein Fortschrittsbalken bei sehr aufwändigen Simulationen).
Dazu ist mir halt einfach noch kein gängiges Konzept eingefallen. Vielleicht hier jemand ne Idee?
Habe dein Beispiel auch mal so umgesetzt, aber da ergeben sich irgendwie Inkonsistenzen in den Daten (sehe ich an der Darstellung) wobei ich da noch nicht genau weiß warum - dürfte ja eigentlich nicht sein, aber es greifen eben mehrere Funktionen auf die Daten zu und wenn ich die alle mit einem Mutex schütze, dann kommt doch die GUI nicht mehr zum Zuge oder?
Naja, die Events in VxWorks waren mehr so eine Art Flag auf die man halt warten konnte ohne CPU-Power zu verschwenden und die man beliebig setzen und zurücksetzen konnte (threadunabhängig!).
Zur Zeit ist es so, dass die Simulation auch nicht laufen brauch, wenn ich an der GUI keine neuen Daten per Slider einstelle. Aktuell ist es so gedacht, dass ich in dem Slot für die Slider die neuen Parameter aus den Slidern ermittle, dann die Kontrolle an den Simulationsthread abgeben möchte und wenn dieser fertig simuliert hat, noch im Slot die neuen Daten anzeigen möchte.
Eine Datenanzeigefunktion in der GUI wäre nicht möglich, da der Aufruf dieser Funktion aus dem Simulationsthread heraus zum Programmabsturz führen würde (Zugriff auf GUI-Funktionen aus Nebenthreads sind nicht möglich).
Ein Signal aus der Simulation heraus zu senden ist auch doof, da ich nicht weiß, wann der Slot ausgeführt wird und ob dann die Daten aktuell sind oder nicht.
Ich müsste also aus meiner GUI heraus die neue Berechnung anstoßen können (weiterer Schleifendurchlauf in der Threadschleife) und dann aus dem Simulationsthread heraus die Darstellung anstoßen können und das immer im richtigen Moment.
Das Problem ist, dass es später auch möglich sein soll, dass jemand anderes eigene GUIs und Simulationsmodelle per Plugin einbinden könnte. Dann kann es sein, dass vielleicht gar keine GUI gebraucht wird, oder aber dass die GUI nur die Darstellung übernimmt oder nur die Steuerung.
Sonst könnte ich die Berechnung ja gleich in dem Slot für die Slider machen. Aber das wäre wieder nicht möglich, wenn die GUI später nur die Darstellung übernehmen soll (zum Beispiel nur ein Fortschrittsbalken bei sehr aufwändigen Simulationen).
Dazu ist mir halt einfach noch kein gängiges Konzept eingefallen. Vielleicht hier jemand ne Idee?
Habe dein Beispiel auch mal so umgesetzt, aber da ergeben sich irgendwie Inkonsistenzen in den Daten (sehe ich an der Darstellung) wobei ich da noch nicht genau weiß warum - dürfte ja eigentlich nicht sein, aber es greifen eben mehrere Funktionen auf die Daten zu und wenn ich die alle mit einem Mutex schütze, dann kommt doch die GUI nicht mehr zum Zuge oder?
Und den Thread pausieren, bis die Daten dargestellt sind, ist keine Option? Also Berechnung anstoßen -> Thread ist an dem Punkt, an dem die Daten cool sind -> Signal an GUI -> pausieren und von der GUI wieder aufgeweckt werden.Kay hat geschrieben:Damit wäre zwar der gleichzeitige Zugriff auf die gemeinsame Ressource verhindert, aber ich kann nicht stuern, ob die GUI nach oder vor der Simulation aktualisiert wird. Im Simulationsthread gibt es nämlich nur eine Stelle, wo alle Daten aktuell sind. Diese sollten dann auch nur in diesem Moment angezeigt werden. Dabei muss die GUI aber halt jederzeit ansprechbar bleiben.
Naja, ich weiß ja nicht um was für ein Objekt es sich handelt, und wie damit gearbeitet wird. Aber wenn auf die Variable 99% der Zeit zugegriffen wird, dann wirst du das Problem immer haben dass die GUI auf das 1% warten muss bis sie die Daten ändern kann. Und genau für sowas ist ein Mutex da.Kay hat geschrieben:
Habe dein Beispiel auch mal so umgesetzt, aber da ergeben sich irgendwie Inkonsistenzen in den Daten (sehe ich an der Darstellung) wobei ich da noch nicht genau weiß warum - dürfte ja eigentlich nicht sein, aber es greifen eben mehrere Funktionen auf die Daten zu und wenn ich die alle mit einem Mutex schütze, dann kommt doch die GUI nicht mehr zum Zuge oder?
Vielleicht lohnt es sich doch, das Objekt zu kopieren, und nicht nur einen Pointer?
-
Troll.Soft
- Beiträge: 190
- Registriert: 18. Juni 2008 09:52
- Wohnort: Hamburg
pfid hat geschrieben:Und den Thread pausieren, bis die Daten dargestellt sind, ist keine Option? Also Berechnung anstoßen -> Thread ist an dem Punkt, an dem die Daten cool sind -> Signal an GUI -> pausieren und von der GUI wieder aufgeweckt werden.
Also im selben Thread wird ein Signal immer sofort ausgeführt.Kay hat geschrieben:Ein Signal aus der Simulation heraus zu senden ist auch doof, da ich nicht weiß, wann der Slot ausgeführt wird und ob dann die Daten aktuell sind oder nicht.
Bei zwei Threads siehe erstes Zitat.
QThreads sind signalsafe und die Lösung von pfid sollte die Deine sein.
tschüß
Troll.Soft
Naja, GUI und Rechenthread laufen aber in unterschiedlichen Threads, weshalb der Ausführungszeitpunkt des Slots nicht deterministisch ist.
Für das eben erwähnte Zitat fällt mir aber auf Anhieb keine sinnvolle Umsetzung ein. Macht ja Sinn, was da steht, aber "Signal an GUI" geht ja schon mal nicht wegen dem mangelnden Determinismus. Unter Nutzung eines Mutex müsste die GUI ja schon darauf warten - sich also bspw. in dem Slot für die Slider befinden. Und wie soll der Rechenthread warten oder dann aufgeweckt werden? Mir fehlt da jetzt ehrlich gesagt die Idee, wie das praktisch umgesetzt werden könnte.
Ich werde morgen mal ein Bsp. posten. Schaffe das jetzt nicht und muss vorher noch ein oder zwei Sachen probieren...
Für das eben erwähnte Zitat fällt mir aber auf Anhieb keine sinnvolle Umsetzung ein. Macht ja Sinn, was da steht, aber "Signal an GUI" geht ja schon mal nicht wegen dem mangelnden Determinismus. Unter Nutzung eines Mutex müsste die GUI ja schon darauf warten - sich also bspw. in dem Slot für die Slider befinden. Und wie soll der Rechenthread warten oder dann aufgeweckt werden? Mir fehlt da jetzt ehrlich gesagt die Idee, wie das praktisch umgesetzt werden könnte.
Ich werde morgen mal ein Bsp. posten. Schaffe das jetzt nicht und muss vorher noch ein oder zwei Sachen probieren...
Punkt 1: Entweder deine GUI wartet per Mutex oder Waitcondition drauf, oder du lässt dir ein Signal schicken. Für eins von beidem musst du dich entscheiden. Die GUI ist in der Regel doch auch nur User-genau, sprich davon abhängig wie schnell oder langsam der User klickt. Wieso ist es dir dann so ultra wichtig dass ein Signal vom Rechenthread möglichst instant verarbeitet wird?Kay hat geschrieben:Naja, GUI und Rechenthread laufen aber in unterschiedlichen Threads, weshalb der Ausführungszeitpunkt des Slots nicht deterministisch ist.
Für das eben erwähnte Zitat fällt mir aber auf Anhieb keine sinnvolle Umsetzung ein. Macht ja Sinn, was da steht, aber "Signal an GUI" geht ja schon mal nicht wegen dem mangelnden Determinismus. Unter Nutzung eines Mutex müsste die GUI ja schon darauf warten - sich also bspw. in dem Slot für die Slider befinden. Und wie soll der Rechenthread warten oder dann aufgeweckt werden? Mir fehlt da jetzt ehrlich gesagt die Idee, wie das praktisch umgesetzt werden könnte.
Ich werde morgen mal ein Bsp. posten. Schaffe das jetzt nicht und muss vorher noch ein oder zwei Sachen probieren...
Wenn du auf Threads und Nebenläufigkeit argumentierst, ist eh überhaupt nichts was du machst deterministisch, da du eh nie weißt, wie deine Threads und Prozesse CPU Zeit vom Scheduler kriegen...
Punkt 2: Zum Beispiel so:
http://qt.nokia.com/doc/4.5/qwaitcondition.html
Code: Alles auswählen
class MyThread
{
public:
void setParameter(void* arg)
{
QMutexLocker m(&mutex);
someMember= arg;
}
void wakeUp()
{
waitCond.wakeOne();
}
protected:
int run()
{
mutex.lock();
doSomethingWith(someMember);
emit calculationDone();
waitCond.wait(&mutex, 30 * 1000); // thread schläft jetzt 30s
// nach dem wakeUp() wird das übergebene mutex automatisch und atomar wieder gelockt
mutex.unlock();
}
QMutex mutex;
QWaitCondition waitCond;
void* someMember;
};Das klingt für mich übrigens nach Waitcondition. Ausserdem wartest du da auch, ich dachte das willst du nicht?Kay hat geschrieben:
Naja, die Events in VxWorks waren mehr so eine Art Flag auf die man halt warten konnte ohne CPU-Power zu verschwenden und die man beliebig setzen und zurücksetzen konnte (threadunabhängig!).
So, ich habe nochmal versucht die Steuerung mittels QWaitCondition umzusetzen. Wenn ich das Programm im Debugger starte (Debuggin /F5 im Creator) läufts auch gut. Wenn ichs aber einfach so starte (Strg + R) dann habe ich immer wieder Inkonsistenzen in den Daten.
Ich versuche hier mal ein Minimalbeispiel des aktuellen Codes zu liefern:
Zuerst der Simulationsthread:
Hier die GUI:
Das hier verwendete System geht aber bspw. nur, wenn auf die Benutzereingabe in der GUI gewartetet werden soll, wenn die Simulation einfach nur durchlaufen soll, müsste das ThreadWait-wait(&Mutex) in der Simulation wieder raus, oder?
(Die WaitConditions sind nach dem Objekt benannt, das warten soll, nicht auf das gewartet wird)
Wie gesagt, das funktioniert nur, wenn es vom gdb ausgeführt wird. Sonst nicht... Hoffe ihr findet den Fehler oder könnt mir bessere Vorschläge machen das zu realisieren (was sicher nicht schwer sein wird
)
Dass der Mutex vor einem WaitCondition-Aufruf gelockt sein musste wusste ich auch noch nicht. Danke!
Zu Punkt 1:
Die GUI soll halt nach Möglichkeit Eingriffe in das System möglichst schnell darstellen und mit immer den aktuellsten Daten! Ggf soll sie aber einfach auch nur einen Fortschrittsbalken oder einen Graphen anzeigen und manchmal kann sie auch fehlen. Durch das Plugin-System ist eben vieles möglich, aber irgendwie muss ich den Zugriff auf die Daten ja kontrollieren. Außer ich überlasse die Thread-Arbeit dem Plugin-Programmierer, was aber schon recht fies wäre, oder?
Der Determinismus ist ja nur wichtig, bei Usereingriffen mit anschließender Änderung der Simulationsdaten und neuer Darstellung. Und damit meine ich eigentlich nur, dass nicht zwei Threads gleichzeitig auf die gemeinsamen Daten zugreifen und die GUI immer die aktuellen Ergebnisse zeigt.
zu Punkt 2:
Warten ist schon Ok. Braucht ja keine Ressourcen. Aber halt nur wenns sein muss. (Die Simulation soll ggf auch ungebremst durchlaufen können, wenn zum Bsp. nur ein Fortschrittsbalken angezeigt wird oder gar nichts)
Das System von den WaitConditions hab ich bis jetzt wohl noch nicht 100%ig durchschaut, weshalb ich deine Aussage mit der Gleichheit zu den Events in VxWorks jetzt nicht unterstreichen oder widerlegen kann...
Hoffe das war jetzt nicht zu viel und jemand hat die Zeit das mal anzuschauen und mir zu sagen, wass ich wider nicht beachtet habe
Danke
Ich versuche hier mal ein Minimalbeispiel des aktuellen Codes zu liefern:
Zuerst der Simulationsthread:
Code: Alles auswählen
class Simulation : public QThread
{
public: Simulation(){//Plugins Data initialisieren
//Signal NewGUI(int) mit Slot ShowGUI(int) verbinden};
protected:
run();
signals:
void NewGUI(int);
private slots:
void ShowGUI(int);
private:
void TuWas();
QMutex Mutex;
QWaitCondition GUIWait;
QWaitCondition ThreadWait;
BigDataBlock *Data;
QList<Plugin*> PluginList1;
QList<Plugin*> PluginList2;
}
void Simulation::TuWas()
{
//Simulationsoperationen mit Data
}
void Simulation::ShowGUI(int i) //Dieser Slot wird im Hauptthread ausgeführt, sonst könnten keine GUI-Elemente gestartet werden!
{
PluginList2[i]->StartGUI(&Mutex, &GUIWait, &ThreadWait);
//Boolean setzen
}
void Simulation::run()
{
bool Fertig=false;
for(int i=0; i<AnzahlSimulationen; i++)
{
emit NewGUI(i);
//Über Boolean warten, bis der Slot ausgeführt wurde und die GUI steht
while(!Mutex.tryLock())
{
msleep(100);
}
while(Fertig!=true)
{
TuWas();
PluginList1[i]->Rechne(Data);
ThreadWait.wait(&Mutex); //an dieser Stelle sind die Daten gültig
GUIWait.wakeOne();
PluginList2[i]->Rechne(Data, &Fertig);
}
Mutex.unlock();
}
}
Code: Alles auswählen
class GUI : public QDialog
{
Q_OBJECT
public:
GUI(){//UserInterface erstellen. Wird von Plugin2 aufgerufen};
void SetWaitParameter(QMutex *WaitMutex, QWaitCondition *WaitCondGUI, QWaitCondition *WaitCondThread);
slots:
//Dieser Slot ist mit allen SliderElementen verbunden und wird bei Änderungen aufgerufen:
void OnSliderChanged(double);
private:
QMutex *Mutex;
QWaitCondition *GUIWait;
QWaitCondition *ThreadWait;
}
//Diese Funktion wird ebenfalls im Plugin2 aufgerufen, welches die in StartGUI()
//übergebenen Parameter einfach an die GUI durchreicht
void GUI::SetWaitParameter(QMutex *WaitMutex, QWaitCondition *WaitCondGUI, QWaitCondition *WaitCondThread)
{
Mutex = WaitMutex;
GUIWait = WaitCondGUI;
ThreadWait = WaitCondThread;
}
//Dieser Slot ist der Knackpunkt:
void GUI::OnSliderChanged(double d)
{
while(!Mutex->tryLock())
{
this->thread()->wait(100); //Da die sleep()-Funktionen von QThread protected sind, wird hier wait() verwendet
}
//...
//Sliderwerte auslesen und in gemeinsamer Variable speichern
ThreadWait->wakeOne();
GUIWait->wait(Mutex);
//Simulationsergebnisse darstellen
Mutex->unlock();
}
(Die WaitConditions sind nach dem Objekt benannt, das warten soll, nicht auf das gewartet wird)
Wie gesagt, das funktioniert nur, wenn es vom gdb ausgeführt wird. Sonst nicht... Hoffe ihr findet den Fehler oder könnt mir bessere Vorschläge machen das zu realisieren (was sicher nicht schwer sein wird
Dass der Mutex vor einem WaitCondition-Aufruf gelockt sein musste wusste ich auch noch nicht. Danke!
Zu Punkt 1:
Die GUI soll halt nach Möglichkeit Eingriffe in das System möglichst schnell darstellen und mit immer den aktuellsten Daten! Ggf soll sie aber einfach auch nur einen Fortschrittsbalken oder einen Graphen anzeigen und manchmal kann sie auch fehlen. Durch das Plugin-System ist eben vieles möglich, aber irgendwie muss ich den Zugriff auf die Daten ja kontrollieren. Außer ich überlasse die Thread-Arbeit dem Plugin-Programmierer, was aber schon recht fies wäre, oder?
Der Determinismus ist ja nur wichtig, bei Usereingriffen mit anschließender Änderung der Simulationsdaten und neuer Darstellung. Und damit meine ich eigentlich nur, dass nicht zwei Threads gleichzeitig auf die gemeinsamen Daten zugreifen und die GUI immer die aktuellen Ergebnisse zeigt.
zu Punkt 2:
Warten ist schon Ok. Braucht ja keine Ressourcen. Aber halt nur wenns sein muss. (Die Simulation soll ggf auch ungebremst durchlaufen können, wenn zum Bsp. nur ein Fortschrittsbalken angezeigt wird oder gar nichts)
Das System von den WaitConditions hab ich bis jetzt wohl noch nicht 100%ig durchschaut, weshalb ich deine Aussage mit der Gleichheit zu den Events in VxWorks jetzt nicht unterstreichen oder widerlegen kann...
Hoffe das war jetzt nicht zu viel und jemand hat die Zeit das mal anzuschauen und mir zu sagen, wass ich wider nicht beachtet habe
Danke
Hab momentan keine Zeit alles zu lesen, daher nur kurz:
Ich ging davon aus, dass nur 1 Waitcond verwendet wird. Die Gui muss niemals künstlich schlafen. Im Slider-Slot wird das Mutex gelockt - das sorgt schon fürs warten. Danach wird der Pointer gesetzt und fertig.
Sobald der Thread mit der Berechnung fertig ist, schickt er ein Signal. Das empfängt die GUI, und kann (in einem separaten Slot!) die Daten darstellen, und den Rechenthread danach direkt wieder aufwecken.
Wenn du inkonsistente Daten bekommst, kommt das vielleicht daher, dass du während der Berechnung die Parameter (BigDataBlock) änderst, aber ich kenn die Berechnung und die Daten nicht, daher kann ich darüber keine Aussage treffen. Das Beispiel von mir sollte das tun was du wolltest - zum beliebigen Zeitpunkt threadsafe die Parameter ändern.
Ich ging davon aus, dass nur 1 Waitcond verwendet wird. Die Gui muss niemals künstlich schlafen. Im Slider-Slot wird das Mutex gelockt - das sorgt schon fürs warten. Danach wird der Pointer gesetzt und fertig.
Sobald der Thread mit der Berechnung fertig ist, schickt er ein Signal. Das empfängt die GUI, und kann (in einem separaten Slot!) die Daten darstellen, und den Rechenthread danach direkt wieder aufwecken.
Wenn du inkonsistente Daten bekommst, kommt das vielleicht daher, dass du während der Berechnung die Parameter (BigDataBlock) änderst, aber ich kenn die Berechnung und die Daten nicht, daher kann ich darüber keine Aussage treffen. Das Beispiel von mir sollte das tun was du wolltest - zum beliebigen Zeitpunkt threadsafe die Parameter ändern.
Hi Leute,
Ich habe jetzt das Konzept nochmal umgebaut. Und zwar so, dass die Darstellung innerhalb eines Slots der GUI erfolgt. (wie oben vorgeschlagen)
Die eigentliche Simulationsschleife sieht jetzt so aus:
Die GUI sieht dann so aus:
Der QMutex "MutexData" ist dabei immer der selbe Mutex (definiert im Simulationsobjekt und in der GUI per Zeiger referenziert)
@pfid: Ist es das so du gemeint hast?
Habe bei dieser Konfiguration aber immer noch zwei Sachen, die mir nicht gefallen:
1.) Um Signal-Slot-Verbindungen zwischen der Simulation und den Plugins herzustellen muss das Plugin-Interface von QObject erben, damit es abstrakte Slots bereitstellen kann, mit denen sich die Simulation verbinden kann. Dadurch wird natürlich die Plugin-Schnittstelle unnötig breit und es entstehen vielleicht sogar Nebeneffekte oder es bieten sich für spätere Pluginprogrammierer ungewollt Möglichkeiten in das Hauptprogramm einzugreifen (Thema: Sicherheit und Stabilität)
2.) Was noch viel schlimmer ist: Ich habe immer noch Inkonsistenzen in den Daten. Die Daten (BigDataBlock) werden während der Simulation eigentlich nur lesend verwendet. Trotzdem ändern sie sich, laut Ausgabe im Qt-Debug-Fenster (qDebug()). Das Verwirrendste ist immer noch, dass ich das Debug-compilierte Programm mittels F5 über den gdb starten kann und es funktioniert einwandfrei und wenn ich die selbe .exe-File als selbstständiges Programm (Strg+R oder aus Explorer) starte, treten die Inkonsistenzen auf. Die Daten ändern sich bei jedem Simulationsdurchlauf. Die Reihenfolge der Aufrufe stimmt aber immer, so wie ich es mir vorgestellt habe (zumindest die Reihenfolge der Debug-Ausgaben)
Werde mich jetzt mal auf die Suche machen, woher die ungewollten Änderungen kommen. Das könnte aber etwas dauern, ehe ich mich mittels qDebug() herangetastet habe. Deswegen, habe ich den Stand jetzt nochmal gepostet. Vielleicht kennt ja jemand das Problem oder sieht einen Designfehler im obigen Beispielcode oder es gibt weitere Vorschläge *hoff*.
Grüße und Danke,
Kay
Ich habe jetzt das Konzept nochmal umgebaut. Und zwar so, dass die Darstellung innerhalb eines Slots der GUI erfolgt. (wie oben vorgeschlagen)
Die eigentliche Simulationsschleife sieht jetzt so aus:
Code: Alles auswählen
MutexData.lock();
while(Fertig!=true)
{
TuWas();
PluginList1[i]->Rechne(Data);
MutexData.unlock();
emit ShowData();
ThreadWait.wait(&Mutex);
PluginList2[i]->Rechne(Data, &Fertig);
}
Code: Alles auswählen
void GUI::OnSliderChanged(double d)
{
while(!MutexData->tryLock())
{
this->thread()->wait(100); //Da die sleep()-Funktionen von QThread protected sind, wird hier wait() verwendet
}
//...
//Sliderwerte auslesen und in gemeinsamer Variable speichern
MutexData->unlock();
ThreadWait->wakeOne();
}
void GUI::OnShowData()
{
while(!MutexData->tryLock())
{
this->thread()->wait(15);
}
//....
//Datan anzeigen
MutexData->unlock();
}
@pfid: Ist es das so du gemeint hast?
Habe bei dieser Konfiguration aber immer noch zwei Sachen, die mir nicht gefallen:
1.) Um Signal-Slot-Verbindungen zwischen der Simulation und den Plugins herzustellen muss das Plugin-Interface von QObject erben, damit es abstrakte Slots bereitstellen kann, mit denen sich die Simulation verbinden kann. Dadurch wird natürlich die Plugin-Schnittstelle unnötig breit und es entstehen vielleicht sogar Nebeneffekte oder es bieten sich für spätere Pluginprogrammierer ungewollt Möglichkeiten in das Hauptprogramm einzugreifen (Thema: Sicherheit und Stabilität)
2.) Was noch viel schlimmer ist: Ich habe immer noch Inkonsistenzen in den Daten. Die Daten (BigDataBlock) werden während der Simulation eigentlich nur lesend verwendet. Trotzdem ändern sie sich, laut Ausgabe im Qt-Debug-Fenster (qDebug()). Das Verwirrendste ist immer noch, dass ich das Debug-compilierte Programm mittels F5 über den gdb starten kann und es funktioniert einwandfrei und wenn ich die selbe .exe-File als selbstständiges Programm (Strg+R oder aus Explorer) starte, treten die Inkonsistenzen auf. Die Daten ändern sich bei jedem Simulationsdurchlauf. Die Reihenfolge der Aufrufe stimmt aber immer, so wie ich es mir vorgestellt habe (zumindest die Reihenfolge der Debug-Ausgaben)
Werde mich jetzt mal auf die Suche machen, woher die ungewollten Änderungen kommen. Das könnte aber etwas dauern, ehe ich mich mittels qDebug() herangetastet habe. Deswegen, habe ich den Stand jetzt nochmal gepostet. Vielleicht kennt ja jemand das Problem oder sieht einen Designfehler im obigen Beispielcode oder es gibt weitere Vorschläge *hoff*.
Grüße und Danke,
Kay
Im Prinzip ja. Jedoch kannst du:Kay hat geschrieben: @pfid: Ist es das so du gemeint hast?
Code: Alles auswählen
while(!MutexData->tryLock())
{
this->thread()->wait(100); //Da die sleep()-Funktionen von QThread protected sind, wird hier wait() verwendet
}
Code: Alles auswählen
MutexData->lock();
Ausserdem hast du im oberen Codeschnipsel wohl einen Tippfehler;
ThreadWait.wait(&Mutex);
sollte
ThreadWait.wait(&MutexData);
heißen.
Zu 1) Ja, entweder du willst das Qt Eventsystem nutzen oder nicht...
Zu 2) Viel Spaß bei der Suche
Hi,
Das sollte eigentlich schon ThreadWait.wait(&Mutex) heißen.
Der MutexData - Mutex ist zum Schutz der Daten und die Waitbedingung ja zum Anstoßen eines weiteren Simulationszyklusses, wenn sich die Daten geändert haben. Durch diesen inhaltlichen Unterschied halte ich zwei Mutexe für sinnvoll. Außerdem gebe ich ja den MutexData vorher frei, wodurch die Waitcondition dann doch gar nicht warten würde, oder?
zu 1.) Das Eventsystem wollte ich eigentlich nicht benutzen, darum das Konstrukt ganz am Anfang, wo in dem Slider-Slot auf die Simulation gewartet wird. Eine schlanke Schnittstelle wäre mir schon lieber gewesen. Naja, mal sehen, mit welcher Lösung ich den Kram in den Griff bekomme.
zu 2.) Ich finde das gar nicht so lustig wie du
Habe bis jetzt zwar nachgewiesen, dass die Daten wirklich korrupt sind, aber noch nicht warum oder wo das passiert 
Das sollte eigentlich schon ThreadWait.wait(&Mutex) heißen.
Der MutexData - Mutex ist zum Schutz der Daten und die Waitbedingung ja zum Anstoßen eines weiteren Simulationszyklusses, wenn sich die Daten geändert haben. Durch diesen inhaltlichen Unterschied halte ich zwei Mutexe für sinnvoll. Außerdem gebe ich ja den MutexData vorher frei, wodurch die Waitcondition dann doch gar nicht warten würde, oder?
zu 1.) Das Eventsystem wollte ich eigentlich nicht benutzen, darum das Konstrukt ganz am Anfang, wo in dem Slider-Slot auf die Simulation gewartet wird. Eine schlanke Schnittstelle wäre mir schon lieber gewesen. Naja, mal sehen, mit welcher Lösung ich den Kram in den Griff bekomme.
zu 2.) Ich finde das gar nicht so lustig wie du
Achso, dachte das wär ein Tippfehler, weil ich das Locken des Mutex 'Mutex' nirgends gesehen habe. Wenn du vor dem emit das unlock weglässt, kannst du in der Waitcondition auch das MutexData Mutex verwenden, was dann automatisch ent- und gelockt wird, dann sparst du dir ein Mutexobjekt. Der GUI Thread sperrt das, wenn ichs richtig sehe, eh gleich wenn er das Signal bekommt.Kay hat geschrieben:Hi,
Das sollte eigentlich schon ThreadWait.wait(&Mutex) heißen.
Der MutexData - Mutex ist zum Schutz der Daten und die Waitbedingung ja zum Anstoßen eines weiteren Simulationszyklusses, wenn sich die Daten geändert haben. Durch diesen inhaltlichen Unterschied halte ich zwei Mutexe für sinnvoll. Außerdem gebe ich ja den MutexData vorher frei, wodurch die Waitcondition dann doch gar nicht warten würde, oder?
Glaub mir, ich weiß wie biestig die Programmierung mit Threads sein kann. Ich hab täglich damit auf verschiedenen Platformen (Linux, Solaris, HP-UX) zu tun, und war schon oft am Rande des Wahnsinnszu 2.) Ich finde das gar nicht so lustig wie duHabe bis jetzt zwar nachgewiesen, dass die Daten wirklich korrupt sind, aber noch nicht warum oder wo das passiert
Hm, ich werde das wohl mit den Mutexen so lassen. Einfach wegen der inhaltlichen Trennung (und weil ich der WaitCondition noch etwas misstraue
). Der Lock des Mutexes für die Waitcondition geschieht vor der while-Schleife und ist wie im vorherigen Bsp., weshalb ichs nicht nochmal hingeschrieben habe.
Habe das Problem -soweit ich das jetzt getestet habe- übrigens gelöst. Und wie so oft war es wieder mal nicht die Ursache, die man vermutet hat. Zumindest nicht direkt...
Jedenfalls hatte ich ein Fehler in der Memberfunktion vom "BigDataBlock". Habe da eine Summe aus den Daten gebildet und nicht immer den Startwert initialisiert. Es ist dann passiert, dass irgendwie Nachbarelemente aus dem Speicher mit durchgesickert sind. Wahrscheinlich weil die temporären Summenspeicher die selbe Größe hatten und der Speicher vorher von den Nachbarelementen (temporär) verwendet wurde. Vielleicht hat der gdb da immer aufgeräumt oder anderen Speicher allokieren lassen, der nur sehr kleine Werte (e-100 und solche Sachen...) enthielt.
Jetzt kann ich mir nochmal überlegen, wie ich das Plugin-Interface genau mache. Ob breit oder schmal
Danke für deine Mühen. Zumindest die Threadsynchronisation scheint jetzt erstmal schön zu sein
Habe das Problem -soweit ich das jetzt getestet habe- übrigens gelöst. Und wie so oft war es wieder mal nicht die Ursache, die man vermutet hat. Zumindest nicht direkt...
Jedenfalls hatte ich ein Fehler in der Memberfunktion vom "BigDataBlock". Habe da eine Summe aus den Daten gebildet und nicht immer den Startwert initialisiert. Es ist dann passiert, dass irgendwie Nachbarelemente aus dem Speicher mit durchgesickert sind. Wahrscheinlich weil die temporären Summenspeicher die selbe Größe hatten und der Speicher vorher von den Nachbarelementen (temporär) verwendet wurde. Vielleicht hat der gdb da immer aufgeräumt oder anderen Speicher allokieren lassen, der nur sehr kleine Werte (e-100 und solche Sachen...) enthielt.
Jetzt kann ich mir nochmal überlegen, wie ich das Plugin-Interface genau mache. Ob breit oder schmal
Danke für deine Mühen. Zumindest die Threadsynchronisation scheint jetzt erstmal schön zu sein
Ich würde dir trotzdem vorschlagen das andere Mutex zu nehmen. Du willst hier genau keine inhaltliche Trennung, du willst eine einzelne Ressource schützen. Der GUI Thread darf nur dann an die Ressource, wenn der Rechenthread nichts tut. Und das synchronisierst du mit dem MutexData Mutex.Kay hat geschrieben:Hm, ich werde das wohl mit den Mutexen so lassen. Einfach wegen der inhaltlichen Trennung (und weil ich der WaitCondition noch etwas misstraue).
Ausserdem stellst du damit automatisch sicher, dass der GUI Thread erst auf die Daten zugreifen kann, wenn das MutexData durch die Waitcondition entsperrt ist, und andersrum der Rechenthread erst weitermachen kann, wenn das MutexData vom GUI Thread entsperrt ist (da die Waitcondition gleich wieder ein Lock macht).