Seite 1 von 2

Pro Worker-Thread eine GUI-Thread?

Verfasst: 21. Juli 2008 23:09
von diddely
Hi,

ich habe bei meiner Aufgabe mit Sensoren zu tun, die ich gelegentlich Abfrage oder irgendwelche Kommandos zu ihnen schicke.
Dabei soll das ganze halt Multithreading-Fähig gemacht werden.
Dafür will ich pro erfassten Sensor einen Thread erzeugen.

Die Daten die ich vom Sensor bekomme, die schickt mein Master-Thread/Prozess (der Koordinator) (bin hierbei noch nicht sicher obs ein thread sein soll) an den jeweiligen Worker-Thread.

Frage1:
Die Daten die im jeweiligen Worker-Thread landen müssen visualisiert werden. (z.B. pro Sensor ein TAB im Hauptdialog)
Ist es sinnvoll pro Worker-Thread eine Gui-Thread zu erzeugen oder reicht der einzige std. GUI-Thread?

Frage2:
Die Daten, die vom Worker-Thread angenommen und eventuell bearbeitet worden sind müssen nun visualisiert werden.
Meine Idee war es die zu visualisierenden Daten an die GUI via Signal/Slot zu verschicken und regelmäßig zu updaten (bei Wertveränderungen usw.).

Frage3:
Das ist bezogen auf Frage 1.
Wenn die von mir genannten Worker-Threads keine Rechenaufgaben bekommen, sondern nur Daten bekommen und diese visualisieren müssen. Oder die Daten, die der Benutzer über die GUI eingibt/verändert an die Sensoren zurückgeschickt werden müssen ( ohne etwas zu berechnen).
Sollte man hierbei auf Worker-Threads verzichten und direkt pro Sensor Gui-Threads erstellen?


Stehe gerade etwas auf dem Schlauch bez. Threading.

Verfasst: 22. Juli 2008 06:34
von Christian81
Es kann nur einen GUI (==Main) - Thread geben.

Verfasst: 22. Juli 2008 09:00
von diddely
Ahh ok danke.

Verfasst: 22. Juli 2008 09:47
von RHBaum
Threads lohnen sich:

- wenn du Dinge unabhaengig nebenher machen willst und der prozessor dafuer geeignetmulticore) ist
- wenn eine langwierige taetigkeit schlecht unterbrechbar gestaltbar ist du aber parralel dazu andere teile des programs laufen lassen und steuern willst.

Die Daten der sensoren "aufbereiten" kostet dich doch kaum rechenzeit oder ist das komplex ? nen eigener thread fuer einen sensor wuerde nur lohnen, wenn es da bissi was zu rechnen gibt.

ob man einen thread fuer die sensoren zum auslesen erstellt, um ihn von der gui zu entkoppeln, wuerd ich davon abhaengig machen, was du mit den sensordaten sonst noch machst.
Loggst du die sensordaten, und sind die logs wichtig, genau so wie die genauigkeit der zeitintervalle zwichen den abrufen, wuerde ich schon nen trhead fuers loggen nen thread fuer die sensoren, und den gui thread erstellen.
willst du die sensordaten nur visualisieren, wuerd ich auf multithreading komplett verzichten und alles ueber timer events loesen. macht viel weniger stresss und wenn gui tot nutzen dir die sensordaten ja auch nix mehr ....

Ciao ...

Verfasst: 22. Juli 2008 10:51
von diddely
Hi,

das aufarbeiten der Sensordaten ist eigentlich nicht komplex.
Es ist aber erwünscht, dass die Threads irgendwann mal "rechnen können".


Die Sensordaten werden noch z.B. geplottet.
Wahrcheinlich (später) noch irgendwelche Kalkulationen.

Die Daten werden, sobald sie anliegen vom Master-Prozess ausgelesen. Der ist sozusagen der Koordinator/ Logger.
Der verteilt die Daten dann an die Threads weiter.

Multithreading wird gefordert, da kommt man nicht drumherum.

Danke für die Antworten.

Verfasst: 22. Juli 2008 11:59
von diddely
hmmm ich glaub ich habe was übersehen.
Threads haben ja einen gemeinsamen Speicher.

Meine Worker-Threads würden sich doch gegenseitig stören?!?
Also die Daten gegenseitig überschreiben....
Aus diesem Grund sollte ich doch lieber Prozesse verwenden.

Oder gibt es ein Wunderwerkzeug von QT, damit ein Speicherbereich doch getrennt werden kann?

Verfasst: 22. Juli 2008 12:11
von Christian81
Natürlich haben sie gemeinsamen Speicher - aber was stört Dich das wenn Du z.B. zwei Instanzen einer Klasse erzeugst?

Verfasst: 22. Juli 2008 14:01
von RHBaum
Aus diesem Grund sollte ich doch lieber Prozesse verwenden.
Unter windows ?

und selbst unter unix, prozessmanagment iss ned einfach, und um die daten in ne gemeinsame gui zu bekommen brauchst IPC ...

Das wuerd ich mir ned antun :-)
Ich behaupt mal das multithreading bei dir scho bissi overkill waer, man koennt deine Anforderungen auch ohne umsetzen, da waer multiprozessing schon irgendwie mit kanonen auf spatzen ...

Ciao ....

Verfasst: 22. Juli 2008 14:04
von diddely
hmm ok, das ist mir irgendwie neu oder ich habe was zuvor verpasst.

Ich war immer der Meinung, immer wenn ich Threads verwende muss ich sie voreinander schützen mit semaphoren oder mutex wie auch immer.
Von Instanzen in Verbindung mit Threads hab ich nie was gesehen/gehört.

Wie wäre denn der Unterschied bei der Erzeugung? (Codebeispiel)


Für mich hört sich die ganze "Instanzsache" so an als ob damit einfach ein neuer Prozess erzeugt wird und kein Thread.... :?:

Evtl könnte ich es am Beispiel eines Codeausschnitts besser verstehen.

Danke dir nochmal für die Mühe

Verfasst: 22. Juli 2008 14:21
von RHBaum
Instanz = variable einer klasse/Typ .... grob gesagt.

grob weil es zu allgemein ist, weil variablen selber auch zeiger, referenzen sein koennen, also durchaus 2 variablen auf die selbe Instanz referenzieren koennen.
eigentlich ein lebendes(instanziiertes) Object von einen Typ/Klasse halt ...

von welcher Programmiersprache kommst Du denn ?

Ciao ...

Verfasst: 22. Juli 2008 14:34
von diddely
schon von C++ :), die Aussage hat mich nur etwas irritiert oder ich hab was falsch verstanden. Deswegen diese komische Antwort.

Ich muss kurz meine Gedanken wieder sammeln ... :)

Wenn ich eine Klasse neu instanziere
x[0] = new Class();
diese ist von Qthread abgeleitet.
Dadurch erzeuge ich automatisch einen Thread.
Den nächsten Thread mit x[1] = new Class() usw....

Soweit so gut

Wenn ich es nun richtig verstanden habe, kann man auch in nur einer Instanz mehrere Threads erzeugen. Wie schaut denn das aus?

Wollte nur die beiden Unterschiede vor Augen habe...mehr nicht ;)

Verfasst: 22. Juli 2008 15:03
von RHBaum
threads haben nichts mit klassen zu tun ...

threads werden vom "System" erzeugt, also man nutzt die API zum system (winapi / windows bzw. Posix bei Linux/Unix ... etc)
diese API's sind meist C .....

Frameworks verhuddeln das meist mit klassen fuer mehr komfort, dafuer weden aber auch details verdeckt.

also generell erzeugst du einen thread aus einem anderen thread heraus.
Beim erzeugen gibst du ihm eine Main procedure als funktionspointer mit, in der der thread startet , sowie eine unspezifizierte variable als context, die alles sein kann ....

das heisst dein thread startet in einer funktion ala void funcXYZ(void * context).
erreicht der thread das ende von funcXYZ ist er auch beendet ....

in der funktionselber hasst du ausser dem context gar nix ... naja und die globalen variablen, aber globale variablen sind ja schmutz.
Also bleibt dem thread nur der context parameter, als Tor zur grossen welt, err zu den daten des programms .... und beim erstellen des threads bestimmst ja, was als parameter genau uebergeben wird, und damit eigentlich was dem thread zur verfuegung steht.

Das ist der C weg .....

QT macht nun folgendes:
Du erstellst eine Instanz von QThread / abgeleite Klasse von QThread.
im Konstruktor wird der thread erzeugt und pausiert .....
einsprungfunktion ist eine statische private memberfunktion(methode) an qthread.
Als context wird gleich ein verweis auf die grade erzeugte instanz (this zeiger im konstruktor) uebergeben ....

mit exec von dem anderen thread aus startest du den thread (resume) ....

in der statischen startmethode wird nix anderes gemacht, als der context zeiger auf QTread gecastet, und an diesem zeiger gleich die methode run aufgerufen ....

Das heisst, fuer dich startet der thread eigentlich gleich mit run() die ja ueberschreiben kannst / sollst .... und die dafuer erzeugte instanz hasst ja gleich zur verfuegung dafuer das in der klasse bist (this zeiger) ....

Das ist natuerlich alles sehr bequem, aber stiftet natuerlich auch werwirrung, da halt viele details der threadprogrammierung im hintergrund laufen ....

WIchtig fuer dich, alle objecte die deine QThread instanz kennt, kennt dann natuerlich der thread, da er die ja ueber den context mit uebergeben bekommt.
Alle member die direkt an qthread haengen, werden von aussen impliziet beim konstruktor erzeugt, und man kommt von aussen ned direkt drauf (datenkapslung).
Da du sicher sein kannst, das die threadproc nie loslaufen wird, bevor die klasse fertig erzeugt wurde (konstructor durchlaufen) brauchst die member nie schuetzen, weil nach der erzeugung nur der thread draufkommt. Oder du schreibst zugriffsfunktionen dafuer, wo dann auch von anderen threads aus zugegriffen werden koennte, ergo -> musst betroffene Daten schuetzen ....
und alles was du lokal in run erzeugst (aufn stack), und ned sharest brauchst natuerlich auch ned schuetzen.
Due kannst deiner Ableitung aber natuerlich auch verweise mitgeben, die musst dann natuerlich schuetzen ....

Also QThread = 1 thread der impliziet erstellt wird.

du kannst in einer instanz natuerlich beliebig viele threads erstellen, und denen sogar unterschiedliche einsprungfunktionen verpassen ....

Macht man z.b. wenn komplexe klassen entkoppeln will und mit nativen c++ abreitet .... also nen thread der eigenstaendig was loggt, nen thread der daten entgegennimmt, nen thread der daten versendet ... etc ... macht manchmal sinn das in einer zu klasse zu bauen ...

unter QT wuerde man das eher anders loesen ... eine Klasse, die mehrere Ableitungen von von qthread instanzieiert (statisch oder dynamisch) , und diese bekommen nen zeiger auf die Instanz in der sie erzeugt wurden.
so koennen dann memberfunktionen von der klasse aus meheren threads aus aufgerufen werden ...

Einigermassen verstaendlich ???

CIao ...

Verfasst: 22. Juli 2008 15:35
von diddely
danke für deine ausführliche erklärung.
werde es heute abend anhand eines beispielprogramms mal testen.

Code sagt mehr als tausend worte :)

Falls du ein Codebeispiel rumliegen hast wäre das natürlich auch ganz gut.
Wenn nicht, ist auch kein Problem.

Muss mich mit Threading etwas mehr auseinandersetzen. :)

Werde meinen Code, hier posten.
Um sicher zu gehen, dass ich es auch richtig verstanden & umgesetzt habe.


danke nochmals

Verfasst: 22. Juli 2008 15:36
von Christian81
Reichen die examples von Qt nicht aus?

Verfasst: 23. Juli 2008 11:40
von diddely
Hab mir nun ein Beispiel aus dem Buch genommen und damit gearbeitet.

main.cpp

Code: Alles auswählen

#include <QtGui/QApplication>
#include "threading.h"

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	Threading w;
	w.show();
	return a.exec();
}


threading.h

Code: Alles auswählen

#ifndef THREADING_H
#define THREADING_H

#include <QtGui/QMainWindow>
#include "ui_threading.h"
#include "thread.h"
#include <QtGui/QCloseEvent>

class Threading : public QMainWindow,Ui::ThreadingClass
{
	Q_OBJECT

public:
	Threading(QWidget *parent = 0, Qt::WFlags flags = 0);
	~Threading();
protected:
	void closeEvent(QCloseEvent *event);

private:
	//Ui::ThreadingClass ui;
	Thread threadA,threadB;

private slots:	
	void getmsg(QString);
	void startThreadA();
	void startThreadB();
};

#endif // THREADING_H

threading.cpp

Code: Alles auswählen

#include "threading.h"

Threading::Threading(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
{
	threadA.setmsg("A");
	threadB.setmsg("B");
	setupUi(this);
	connect(m_starta,SIGNAL(clicked()),this,SLOT(startThreadA()));
	connect(m_startb,SIGNAL(clicked()),this,SLOT(startThreadB()));
	connect(&threadA,SIGNAL(sendmsg(QString)),this,SLOT(getmsg(QString)));
connect(&threadB,SIGNAL(sendmsg(QString)),this,SLOT(getmsg(QString)));
}

Threading::~Threading()
{

}

void Threading::startThreadA()
{
	if(threadA.isRunning()){
		threadA.stop();
		m_starta->setText("Start A");
	}else{
		threadA.start();
		m_starta->setText("Stop A");
	}
}

void Threading::startThreadB()
{
	if(threadB.isRunning()){
		threadB.stop();
		m_startb->setText("Start B");
	}else{
		threadB.start();
		m_startb->setText("Stop B");
	}

}

void Threading::closeEvent(QCloseEvent *event)
{
	threadA.stop();
	threadB.stop();
	threadA.wait();
	threadB.wait();
	event->accept();

}	

void Threading::getmsg(QString msg)
{
	m_list->addItem(msg);
}

thread.h

Code: Alles auswählen

#ifndef THREAD_H
#define THREAD_H

#include <QThread>
#include <QString>

class Thread : public QThread
{
	Q_OBJECT

public:
	Thread();
	~Thread();
	void stop();
	void setmsg(const QString &msg);

protected:
	void run();

signals:
	void sendmsg(QString);

private:
	volatile bool stopped;
	QString message;
	
};

#endif // THREAD_H


thread.cpp

Code: Alles auswählen

#include "thread.h"

Thread::Thread()
{
	stopped = false;

}

Thread::~Thread()
{

}

void Thread::run()
{
	int i=0;
	while(!stopped)
		i++;
//		emit sendmsg(message);
	stopped = false;
		
}

void Thread::stop()
{
	stopped = true;
}

void Thread::setmsg(const QString &msg)
{
	message = msg;
}
Die Threads schicken aus dem jeweiligen run() ein Signal an das gui-thread damit von da in das listwidget "A" oder "B" eingetragen werden kann. Habe dafür einen Slot getmsg geschrieben.
So wollte ich sehen, wer von den Threads arbeitet.

Die ganze Sache hängt aber ein bisschen, so habe ich das emittieren weggelassen und einfach eine Variable i hochzählen lassen.
Will damit eine nahezu realistische Auslastung simulieren.

Ist da was falsch an meiner Vorgehensweise?
Warum hängt dauernd meine GUI?

EDIT: Ich denke mal, dass beide Sachen einfach zu viel Auslastung verursachen. Habe ein Sleep in run eingeführt. So kann ich sehen, dass die Threads arbeiten.