Multithreadprogrammierung - Verständnisfrage

Alles rund um die Programmierung mit Qt
Antworten
M@g!ndo$ch
Beiträge: 70
Registriert: 7. Januar 2007 14:56

Multithreadprogrammierung - Verständnisfrage

Beitrag von M@g!ndo$ch »

Hi,

ich würde mich gerne mit eurer Hilfe versichern, dass ich das Thema Threads und Signals/Slots richtig verstanden habe.

Folgendes Szenario:

Thread1 empfängt Daten an einem UdpSocket. Diese schickt er als QByteArray mittels Signal an Thread2 weiter. Signal und Slot sind nicht mittels Qt::DirectConnection verbunden:

Code: Alles auswählen

result = connect(thread1,SIGNAL(datagramReceivedSignal(QByteArray)),thread2,SLOT(rxDatagramSlot(QByteArray)));
In dem Slot wird das QByteArray in eine MemberVariable von Thread2 kopiert und Thread2 wird aufgeweckt:

Code: Alles auswählen

void Thread2::rxDatagramSlot(QByteArray oDatagram)
{	
	//store datagram
	this->m_pRxDatagram = new QByteArray(oDatagram);
	
	if (!isRunning()) 
	{
         start(LowPriority);
    } 
    else
    {
         m_bRxDataAvailable = true;
         m_oWaitCondition.wakeOne();
    }
}
In der run()-Methode von Thread2 wird dann das QByteArray weiterverarbeitet:

Code: Alles auswählen

void Thread2::run()
{			
	forever
	{		
		//wait
		m_oWaitCondition.wait(&m_oMutex);
		
		if(m_bRxDataAvailable == true)
		{	
		
			//do sth. useful 

			m_bRxDataAvailable = false;
		}
	}
}



Wenn ich die Doku richtig verstanden habe, muss ich den Code im Slot und den Code in run() nicht mit einem Mutex schützen. Wenn Thread2 gerade wartet, wenn Thread1 das Signal schickt, führt Thread2 zunächst den Code im Slot aus (keine DirectConnection) und erst danach den Code in run(). Wenn Thread1 das Signal schickt, wenn Thread2 gerade in run() zu Gange ist, wird erst der code bis zu wait() ausgeführt und dann der Slot.

Ist das richtig?

Wenn ich Signal und Slot mit Qt::DirectConnection verbunden hätte, müsste ich den Code mit einem Mutex schützen, denn dann könnte die Membervariable von Thread1 im Slot verändert werden, solange Thread2 gerade run() abarbeitet.

Korrekt?

Bin mir nicht ganz sicher ob mein Verständnis richtig ist, da ich in der Doku nicht gefunden habe was gilt, wenn sowohl das Sender-Objekt als auch das Empfänger-Objekt selbst ein Thread ist und es sich nicht um Objekte handelt, die eben in unterschiedlichen Threads leben.

Gruß M@g
Zandru
Beiträge: 84
Registriert: 29. Mai 2007 15:35

Beitrag von Zandru »

also nach meinem Verständnis kann das nicht klappen.

Warum? Weil der 2. Thread seine eigene Event-Loop braucht, um die Signals zu verarbeiten, die an ihn gesendet werden.

Dein Code sieht eher so aus wie man es machen würde, wenn man keine QueuedConnection verwendet (sondern direkte Funktionsaufrufe).

Sprich, was fehlt ist die EventLoop in deinem run() und mit einer WaitCondition wirds so auch nicht klappen, da die Eventloop den Slot aufrufen muß.

Hoffe das hat dir zumindest etwas geholfen :)

Links zum Thema:
http://doc.trolltech.com/4.3/threads.ht ... ss-threads

http://doc.trolltech.com/4.3/threads-mandelbrot.html (hier ist render() *KEIN* Slot! Nur deswegen geht das mit der Waitcondition...)
M@g!ndo$ch
Beiträge: 70
Registriert: 7. Januar 2007 14:56

Beitrag von M@g!ndo$ch »

Hi,

danke für die Antwort! Aber ehrlich gesagt versteh ich nicht wie mein Programm dann bis jetzt funktioniert hat^^

Ich hab mich an dem Manelbrotbeispiel orientiert und mir ist klar, dass dort ein direkter Funktionsaufruf verwendet wird. Allerdings will ich vermeiden, dass die beiden Threads etwas voneinander wissen müssen. Daher wollte ich Signals und Slots verwenden um die "Komunikation" zwischen den Threads zu realisieren.

Ich rufe in Thread2 nirgends exec() auf, daher dürfte doch auch die EventLoop nicht aktiv sein, oder? In Thread1 wird die EventLoop allerdings angeworfen, da ich da einen QTimer verwende.

Hab mir die Doku nochmal etwas genauer angeschaut:
Like other objects, QThread objects live in the thread where the object was created -- not in the thread that is created when QThread::run() is called.
Ich glaube ich dachte nur, dass ich QueuedConnections verwende ^^

Die beiden Threads werden im MainThread erzeugt, leben folglich im selben Thread. Da ich bei connect() keine Parameter übergeben habe werden wohl Auto Connections verwendet:
With auto connections (the default), the behavior is the same as with direct connections if the signal is emitted in the thread where the receiver lives; otherwise, the behavior is that of a queued connection.
Da Sender und Empfänger des Signals wohl im selben Thread leben,
verhält sich die Connection wie eine Direct Connection. Daher muss ich ne' Mutex verwenden, weil der Slot von Thread1 ausgeführt wird....

Aber das mit der EventLoop in Thread2 verstehe ich nicht!!!

In der Doku steht:
An event loop in a thread makes it possible for the thread to use certain non-GUI Qt classes that require the presence of an event loop (such as QTimer, QTcpSocket, and QProcess). It also makes it possible to connect signals from any threads to slots of a specific thread.
Warum empfängt denn dann Thread2 das Signal wenn ich die EventLoop gar nicht gestartet habe ?!?
Zandru
Beiträge: 84
Registriert: 29. Mai 2007 15:35

Beitrag von Zandru »

Wenn Thread2 keine Eventloop hat, kann er auch keine queued-connection slots ausführen.

Vermutlich werden die Slots in Thread1 aufgerufen, nicht in Thread2.

Verwirrend dabei ist, das das Thread2-Objekt trotzdem Thread1 zugeordnet ist, solange du das nicht mit moveToThread http://doc.trolltech.com/4.3/qobject.html#moveToThread in Ordnung bringst.

(Und solange du das nicht machst, wird auch eine Direct-Connection benutzt, da ja das Thread2-Objekt in Thread1 "lebt")

Hilft dir das?
M@g!ndo$ch
Beiträge: 70
Registriert: 7. Januar 2007 14:56

Beitrag von M@g!ndo$ch »

Zandru hat geschrieben: Vermutlich werden die Slots in Thread1 aufgerufen, nicht in Thread2.
Genau das wollte ich hiermit sagen:
M@g!ndo$ch hat geschrieben: Da Sender und Empfänger des Signals wohl im selben Thread leben,
verhält sich die Connection wie eine Direct Connection.
Zandru hat geschrieben: Verwirrend dabei ist, das das Thread2-Objekt trotzdem Thread1 zugeordnet ist, solange du das nicht mit moveToThread http://doc.trolltech.com/4.3/qobject.html#moveToThread in Ordnung bringst.
Warum ist das Thread2-Objekt dem Thread1 zugeordnet?

Die beiden Objekte werden doch im HauptThread erzeugt:
M@g!ndo$ch hat geschrieben: Die beiden Threads werden im MainThread erzeugt, leben folglich im selben Thread.
Hier der Code:

Code: Alles auswählen

thread1 = new Thread1();
thread2 = new Thread2();
result = connect(thread1,SIGNAL(datagramReceivedSignal(QByteArray)),thread2,SLOT(rxDatagramSlot(QByteArray)));
Es wird also wohl tatsächlich eine Direct Connection verwendet!

Muss man die EventLoop nur starten, wenn man tatsächlich Queued Connections zwischen zwei Threads verwenden möchte, die nicht im selben Thread erzeugt wurden?

Gruß M@g
Zandru
Beiträge: 84
Registriert: 29. Mai 2007 15:35

Beitrag von Zandru »

Sorry, ich dachte Thread1 sei der Mainthread. Du hast also 3 Threads.

ein beliebiges QObject ist immer dem Thread zugeordnet, das seinen Constructor aufruft. QThread wird hier nicht anders behandelt, darum sind sie nicht "sich selbst" sondern dem Mainthread zugeordnet.

In der Doku zu Qt::AutoConnection steht:
"If the signal is emitted from the thread in which the receiving object lives, the slot is invoked directly, as with Qt::DirectConnection; otherwise the signal is queued, as with Qt::QueuedConnection."

D.h. wenn du in Thread1 oder Thread2 signale emittierst, werden keine Direct-Connections verwendet, da die Objekte im Mainthread "leben".
Die Slots werden dann im Mainthread gehandelt. Wohl kaum Threadsafe, und bestimmt nicht das, was du willst.

Aber selbst wenn du Direct connections hinbekommen würdest - die sind auch nicht threadsafe!

Es bleibt dir nur es "richtig" zu machen, also die Thread-Objekte mit moveToThread sich selbst zuzuordnen und allen Threads die slots haben eine eventloop zu spendieren.

Oder auf signal&slots zu verzichten.
M@g!ndo$ch
Beiträge: 70
Registriert: 7. Januar 2007 14:56

Beitrag von M@g!ndo$ch »

Aha, thx erst mal.

Ich werds mir nochmal mal bei Gelegenheit zu Gemüte führen ;-)

Gruß M@g
Antworten