Fragen zur Thread-Programmierung

Alles rund um die Programmierung mit Qt
Antworten
nierth
Beiträge: 30
Registriert: 19. November 2008 22:56

Fragen zur Thread-Programmierung

Beitrag von nierth »

Hallo,

da ich mich gerade mit Multithreading beschäftige, ein paar Fragen.
Hintergrund ist der: Ich lese Daten von einem CAN2USB-Dongle ein, speichere diese zwischen und lese sie anschließend aus um sie anzeigen zu können. Für diese Aufgabe stelle ich mir zwei Threads vor, welche sich gegenseitig über mutex sperren können.

Meine Fragen im Konkreten:
- was ist genau der Unterschied zwischen run() und exec() und was hat es mit den beiden Funktionen auf sich? Ich habe von Java und anderen echtzeitfähigen Programmiersprachen noch im Hinterkopf, dass sich im Kern jedes Threads eine while(true)-Schleife befindet. Dem ist in den Qt Beispielen allerdings nicht so.
- wie realisiere ich am geschicktesten den Aufruf von QPainter um die Daten zu zeichnen? Bisher habe ich testweise einfach einen Timer herunterlaufenlassen und dann die update()-Funktion aufgerufen. Soll ich weiterhin in dem Thread einfach die update()-Funktion aufrufen? Oder einfach einen neuen im Thread erstellen und dann zeichnen lassen? Hierbei weiß ich allerdings nicht, wie sich Qt verhalten wird, da man ja QPainter immer wieder schließen soll.
- ist es besser die beiden Threads über Signale und Slots zu sychronisieren als über mutex?

Danke für die Antworten,

Thomas
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Hast Du Dir die Doku zu QThead schonmal durchgelesen? Da müsste Deine erste Frage beantwortet sein.
Zu Deiner zweiten Frage - Du kannst nur im Main-Thread zeichnen.
Was genau meinst Du mit Synchronisation?
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
nierth
Beiträge: 30
Registriert: 19. November 2008 22:56

Beitrag von nierth »

zur ersten Frage: ja, ich habe mir die Doku durchgelesen - ganz klar ist es mir allerdings immer noch nicht.
Bezüglich Synchornisation meine ich das Problem, dass der eine Thread die Daten nicht lesen darf während der andere sie gerade in die Variable schreibt.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

void QThread::run () [virtual protected]
The starting point for the thread.

int QThread::exec () [protected]
Enters the event loop ...
Was ist daran unverständlich?

Zu dem anderen Problem - wie sollte das mit Signals/Slots überhaupt funktionieren?
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
nierth
Beiträge: 30
Registriert: 19. November 2008 22:56

Beitrag von nierth »

wenn ich die Funktion run() neu implementiere, ohne ein exec() hineinzuschreiben - was passiert dann? Habe ich einen Thread ohne Schleife? Was genau bedeutet event loop?
Ansonsten dachte ich an ein gegenseitiges Aufrufen zweier Funktionen - sobald die erste geschrieben hat wird ein Signal gesendet damit die zweite auslesen kann...

Thomas
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Wenn Du keine Event-Loop in deinem Thread hast, wird es auch nichts mit signals und slots --> mal wieder Verweis auf die Doku und Examples ( http://doc.trolltech.com/4.4/threads.html )
Für deine Synchronisation - das geht natürlich auch mit Signals/Slots, aber dafür brauche ich nicht unbedingt einen extra-Thread.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
VuuRWerK
Beiträge: 82
Registriert: 11. Juni 2007 20:46
Wohnort: Dresden

Beitrag von VuuRWerK »

Wegen der Geschichte ein Thread darf erst was machen wenn der zweite fertig ist und damit auch etwas bereitstellt womit der erste Thread was anfangen kann, suchst Du wahrscheinlich das Design Pattern Semaphore, Beispiel mit Qt: http://doc.trolltech.com/4.4/threads-semaphores.html

Die Synchronisierung wie Du sie aus Java kennst ist aber etwas anderes, da geht es mehr darum eine Methode "Thread-Safe" zu machen, also alle anderen Threads müssen warten bis ein Thread der sich gerade "in" der Methode befindet fertig ist um so zu verhindern das 2 Threads zur "selben" Zeit Daten unterschiedlich verarbeiten. Das erreichst Du mit QMutex und QWaitCondition

Gut Schuß
VuuRWerK ;)
Es gibt nur 3 natürliche Feinde des Programmierers: Tageslicht, frische Luft und das unerträgliche Gebrüll der Vögel.
Oft ist die Ursache des schwarzsehens lediglich ein verrutschen des Bretts vorm Kopf =)
Chris81T
Beiträge: 82
Registriert: 4. Mai 2008 00:06
Wohnort: Urbar

Beitrag von Chris81T »

wenn ich die Funktion run() neu implementiere, ohne ein exec() hineinzuschreiben - was passiert dann? Habe ich einen Thread ohne Schleife?
Dann wird die run() wieder verlassen und an dem Punkt weitergemacht, wo der besagte Thread gestartet wurde.

Wenn mit exec() die Thread Eventloop gestartet wird, "lebt" der Thread dann noch und kann mit signals / slots bedient werden. Wenn die Eventloop beendet wird, hätte man die Möglichkeit, den "returnCode" in der run() auswerten zu lassen und falls auch dann keine weiteren Anweisungen in der run() vorhanden sind, wird diese dann auch verlassen.

Vielleicht hilfts ja besser zum Verständnis. Aber wie die Anderen schon sagten: Qt Assistent lesen, der wirlklich top ist / oder auch mal die Examples / Qt Demos laufen lassen...

Wichtig ist aber auch noch, dass man sich Gedanken darum macht, wo genau Objekte in welchem Thread "leben", sag ich mal. Dazu verweis ich direkt mal auf die Doku zur Klasse QObject.

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

Beitrag von Superheftig »

Der Event Loop von qt bewirkt bewirkt dass der thread nachrichten empfangen kann und wird mit exec gestartet.
Wenn du die run() methode im Thread nicht überschreibst sieht das ganze standardmäßig so aus

Code: Alles auswählen

void run() {
  exec();
}
Der Event Loop wird also sofort aufgerufen. Der EventLoop blockiert den Code an dieser stelle, ist also so eine art while(true) konstruktion. wenn du den thread mit exit() stoppst wird diese schleife durchbrochen und der code kehrt in die run medthode zurück.
Wenn du die run methode überschreibst und du das Event Managment von qt braucht muss der letzte aufruf in deiner run Methode exex() sein. Sonst ist es dem Thread nicht möglich nachrichten zu empfangen, also

Code: Alles auswählen

void run() {
/* 
  Thread initialisierung
     Code hier
*/
  exec()
}
Im normalfall brauchst du allerdings das exec() garnicht. Dann kannst du deinen eigenen loop machen, also

Code: Alles auswählen

void run() {
  while(true) {
     // Daten vom usb auslesen und in queue speichern
     // falls keine Daten vorhanden sind sleep(10);
  }
}
Alles was mit Fenstern und zeichnen zu tun hat kannst du nur im main Thread wie oben erwähnt. Also startest du einfach aus dem mainthread den thread der die daten von usb ausliehst und übergibst ihm ne Queue als pointer. Auf die Queue haben dann beide Threads zugriff darum schützt du jeden zugriff durch einen Mutex.....Fertig!

Signals und Slots brauchst du also garnicht
nierth
Beiträge: 30
Registriert: 19. November 2008 22:56

Beitrag von nierth »

danke für das ausführliche Beispiel, genau da hat es bei mir gehakt!
nierth
Beiträge: 30
Registriert: 19. November 2008 22:56

Beitrag von nierth »

Hallo,

mittlerweile bin ich soweit, dass ich das Einlesen zyklisch als Thread ausführe und alle 100ms ein Signal zum Weiterrechnen/Zeichnen schicke. Das Rechnen/Zeichnen ist jedoch nicht als Thread ausgeführt. Was passiert wenn er nun rechnet? blockiert er hierbei den Einlesethread (weil es selber ja keiner ist)?

Thomas

P.S. sehe ich es rihtig das lediglich die Argumente, welche in run() stehen, als Thread ausgeführt werden, andere Funktionen (auch wenn sie sich in derselben Klasse befinden) jedoch nicht?
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag von Superheftig »

Du hast automatisch den main Thread und deinen Auslese Thread. Das Zeichnen/Rechnen blockiert also das auslesen nicht.

Alle funktionen werden von dem Thread ausgeführt von dem sie aufgerufen werden. Wenn du also eine Funktion die in deinem AusleseThread implementiert ist von außerhalb aufrufst, also z.b aus deiner Zeichnen/Rechnen Funktion, dann läuft die Funktion auch in diesem Thread. Dieses sollte man allersings unterlassen, da bei eingigen QT Klassen das verhalten ungewiss ist.
Warum und Welche steht in der Doku
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

@nierth

Dir fehlt es noch ein bisserl an Verstaendniss zu den Threads. Du solltest mal versuchen, nen "Thread" mit der winapi zum laufen zu bekommen, ohne QThread ... dann verstehst du auch besser was welche Methode in QThread macht und in welchem thread sie lebt.
QThread repraesentiert nicht ein Thread, wie man es anhand des namens vielleicht annehmen koennte, sondern implementiert das Thread haendling, also viele funktionen rund um das erzeugen und Steuern des Threades.
P.S. sehe ich es rihtig das lediglich die Argumente, welche in run() stehen, als Thread ausgeführt werden
Ja, nur Deine überschriebene run() methode wird initial in deinem neuen thread ausgefuehrt, wenn die run methode zu ende (also du den scope der funktion verlaesst) ist, ist dein thread beendet, und wird auch vom system freigegeben (das thread handle wird ungültig) .
Naruerlich werden alle aufrufe von deiner run() methode aus dann in dem thread ausgefuehrt, egal welche methode von welcher klasse das ist.


- ist es besser die beiden Threads über Signale und Slots zu sychronisieren als über mutex?
Das ist ne ziemlich komplexe frage und nicht mit einem einfachen ja oder nein zu beantworten.

wichtig ist, das du folgendes verstehst:
signale und slots werden ueber die Eventloop synchronisiert, in dessen Context das Object mit den Slots laeuft.
Dabei laufen alle Objecte im Context der Application (QApplication) und nur Objecte die eine eigene Eventloop starten koennen (QThread::exec() z.b.) , koennen aus diesem context wechseln. Die Eventloop lauft natuerlich in dem threrad, in dem sie gestartet wurde, also sollt man wenn man parralelle eventloops haben will, die 2. loop in nem eigenen thread starten, (deshalb im run() ausgefuehrt).
Startet man 2 eventloops im selben thread, wird nur die zueletzt aufgerufene bearbeitet, und erst wenn die beendet wird, gehts in der alten weiter. In einigen faellen wird dies verwendet (Modelitaet) ....

- bei signalen und slots ueber slots werden die parameter wie bei jeder funktion kopiert. Bei queued connections wirden die parameter mit in die eventloop kopiert und irgendwann spaeter ausgefuehrt.
Normal verwendest da eigentlich impliziet immer kopien, was das ganze in sachen threadsicherheit etc ziemlich einfach macht.
Verwendest du da zeiger und referenzen, musst du sicherstellen das die
a. geschutzt sind und b, atuerlich noch existieren, wenn die eventloop die dinger abarbeitet.
Wenn Du keine Event-Loop in deinem Thread hast, wird es auch nichts mit signals und slots
Das ist natürlich nur halb richtig ....
du kannst in deinem thread ohne eventloop signale correct emitten, mit einem postevent werden die korrect in die eventloop deines Mainthreads der QApplication eingetragen, und dein Slot wird auch angsprungen ....
Also mit QThread und ohne eigene Loop kannst nur signale zum mainthread schicken, aber keine vom maintread empfangen ....

Selber synchroniseren macht Sinn wenn man viele datenmengen bekommt und die ned kopieren will ... ausserdem ist der eventmechanismus ziemlich generisch und damit ned wirklich schnell ...

Mit einem Can Adapter wirst sicherlich performancetechnisch keine probleme haben ....
Selbst mit 4 Highspeed CAN's (500kbit) parrallel hier langweilt sich der rechner ...
Aber hab mal Flexray und Most, da siehts schnell anders aus ... und dann wird man sich um performance gedanken machen müssen.

Ciao ...
Antworten