QThread: finished-signale kommen nicht an

Alles rund um die Programmierung mit Qt
graythornAK
Beiträge: 29
Registriert: 16. März 2007 15:14

QThread: finished-signale kommen nicht an

Beitrag von graythornAK »

Hallo zusammen,

ich verzweifle bei der Thread-Programmierung. VIelleicht wißt ihr ja Hilfe. Ich habe ein sehr großes Projekt, aber ich versuche mal die Klassen die Probleme bereite zu skizzieren. Sämtliche Thread-spezifischen Funktionen sind enthalten, alles andere wurde der Übersicht halber weggelassen. Ich hoffe es ist verständlich.

Das Problem ist: Ich erstelle QThreads innerhalb eines QThread. Das finished()-Signal der im QThread erzeugte Qhreads kommt nicht.
Ich habe eine Basisklasse für Threads, davon sind dann die eigentlichen Thread-Klassen abgeleitet.

Hier die Klassen:

Die Basisklasse für Threads:

Code: Alles auswählen

class WorkThread : public QThread
{
	Q_OBJECT

	protected:
		WorkThread()
		{
			connect(this, SIGNAL(finished()), this, SLOT(OnFinished()));
		}

	public:
		virtual void StartThread()
		{
			start();
		}

	private:
		virtual void run()
		{
			m_bRunSuccess = OnRun();
		}

	private slots:
		void 				OnFinished()
        {
			OnThreadFinished();
        }

	protected:
		virtual bool OnRun() = 0;		//< true if successfully run
		virtual bool OnThreadFinished() = 0;
}; // class WorkThread

Folgende Implementirung funktioniert einwandfrei. Nach Beendigung der OnRun() Methode wird OnThreadFinished() aufgerufen.

Code: Alles auswählen

main()
{
	Geht	*pGeht = new Geht();
	pGeht->StartThread();
}

class Geht : public WorkThread
{
	public:
		Geht : WorkThread()
		{
		}

	protected:
		virtual bool OnRun()
		{
			....
			mach was;
			...

			return true;
		}
		
		virtual bool OnThreadFinished()
		{
			...kommt hier an !!!!
			
			return true;
		}
};


Jetzt wirds komplizierter. Es gibt zwei Threadklassen, MainThread und SubThread. MainThread erzeugt die SubThreads und wartet darauf, das die sich beenden. Die OnFinished()-Methode der SubThreads wird allerdings nicht aufgerufen, obwohl die OnRun()-Methode definitiv beendet wird:

Code: Alles auswählen

class MainThread : public WorkThread
{
	public:
		MainThread : WorkThread()
		{
		}

	protected:
		virtual bool		OnRun()
		{
			SubThread *pSubThread = new SubThread()
			pSubThread->StartThread();
			
			// warte auf SubThread...
			while (pSubThread not finished)
			{
			  SchlafeEinWenig...
			}

			return true;
		}

		virtual bool OnThreadFinished()
		{
			return true;
		}
};

Code: Alles auswählen

class SubThread : public WorkThread
{
	public:
		SubThread : WorkThread()
		{
		}

	protected:
		virtual bool OnRun()
		{
			tu was........
			
			return true;
		}
		
		virtual bool OnThreadFinished()
		{
			!!! kommt hier nie an, also wird
			!!! offensichtlich das  finished-signal der von der
			!!! Basisklasse nicht emittiert
			
			return true;
		}
};
Die OnRun-Methode von SubThread wird definitv aufgerufen und beendet, die OnThreadFinished-Methode aber nicht.

Vielen Dank für eure Mühe und hoffentlich baldige Hilfe :D
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Nach nur nem kurzen Blick: Könnte es daran liegen, dass der virtual slot private und nicht protected ist?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Mich würde interessieren, wie du ermittelst ob pSubThread fertig ist, das geht aus deinen Codefetzen nicht hervor. Evtl. liefert die Prüfung immer false, sodass MainThread::OnRUn() nie fertig wird und somit auch NIE ein finished werfen wird.

Du könntest auch versuchen, ein minimales, lauffähiges Beispiel zusammenzustellen und hier gezipped anhängen. Dann kann man genauer sehen was du treibst :P und vor allem mit eigenem frickeln eine Lösung herbeizaubern.
graythornAK
Beiträge: 29
Registriert: 16. März 2007 15:14

Beitrag von graythornAK »

Nach nur nem kurzen Blick: Könnte es daran liegen, dass der virtual slot private und nicht protected ist?
Ich habe den Slot mal protected gemacht, gleiches Ergebnis, daran lag es also nicht!

Wäre auch unlogisch, da es dann ja in beiden Fällen nicht funktionieren würde. Der Aufruf des Slots findet ja auch nur innerhalb der basisklasse statt.
graythornAK
Beiträge: 29
Registriert: 16. März 2007 15:14

Beitrag von graythornAK »

franzf hat geschrieben:Mich würde interessieren, wie du ermittelst ob pSubThread fertig ist
Ich habe einen breakpunkt an das Ende der OnRun-Methode von SubThread gesetzt, da kommt das Programm an.

franzf hat geschrieben: Du könntest auch versuchen, ein minimales, lauffähiges Beispiel zusammenzustellen und hier gezipped anhängen. Dann kann man genauer sehen was du treibst :P und vor allem mit eigenem frickeln eine Lösung herbeizaubern.
Ist extrem aufwendig. Es besteht aus einem Hauptprogram und 4 Bibliotheken, alles zusammen etwa 250 Module. Das macht wenig Spaß....
Sollte ich keine Lösung finden oder bekommen, kann ich es ja dennoch mal versuchen.....
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Das obige Beispiel so weit zu verändern das es kompiliebar ist wird ja wohl nicht das Problem sein.
Aus de Code geht nicht hervor was falsch sein sollte zumal bei class Geht und class SubThread exakt das Gleiche steht und einmal soll es gehen und einmal nicht - das ist nicht logisch...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Aus de Code geht nicht hervor was falsch sein sollte zumal bei class Geht und class SubThread exakt das Gleiche steht und einmal soll es gehen und einmal nicht - das ist nicht logisch...
Einen kleinen Unterschied gibt es: der MainThread lebt in der GUI und hat somit eine Eventloop. Die SubThreads leben im MainThread und können daher keine Eventloop nutzen (weil sich der MainThread im unnützen "while-Loop" befindet).

Aber ich glaube nicht, dass das zum Problem führt.. denn eigentlich müssten doch beide connects eine DirectConnection ergeben und somit überhaupt keinen Eventloop benötigen..
Ist extrem aufwendig. Es besteht aus einem Hauptprogram und 4 Bibliotheken, alles zusammen etwa 250 Module. Das macht wenig Spaß....
Aufwendig? Wir brauchen doch nichts weiter als eine funktionierende main und die drei Thread-Klassen (BasisKlasse, MainThread und SubThread).
Ich würde ohnehin empfehlen, solche Konzepte immer im Kleinen zu testen und die Funktion (das Threadmanagement) zu verifizieren, dass macht die Fehlersuche einfacher...

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

Beitrag von Christian81 »

solarix hat geschrieben: Einen kleinen Unterschied gibt es: der MainThread lebt in der GUI und hat somit eine Eventloop. Die SubThreads leben im MainThread und können daher keine Eventloop nutzen (weil sich der MainThread im unnützen "while-Loop" befindet).
Stimmt - Slots gehen natürlich nur wenn eine Event-Loop da ist was nicht der Fall ist. Da ists egal ob Direct oder Queued Connection.
Zuletzt geändert von Christian81 am 5. Januar 2011 13:35, insgesamt 1-mal geändert.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
graythornAK
Beiträge: 29
Registriert: 16. März 2007 15:14

Beitrag von graythornAK »

solarix hat geschrieben:
Aufwendig?
...ja ist es, aber ich werde es wohl machen.... :)


Das Problem ist, das die WorkThread Basisklasse in verschiedenen Libraries hatt, also:

lib1: class ThreadInterface...

lib2: QtTreadInterface : public lib1::ThreadInterface...

lib3: WorkThread : public lib2::QtTreadInterface

könnte das evtl ein Prblem sein???
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

graythornAK hat geschrieben:könnte das evtl ein Prblem sein???
Sicher, wenn du in lib1 oder lib2 eine Änderung vornimmst, lib3 (oder dein Programm) aber eine andere (ältere) Version der Lib verwendet (passiert z.B. ganz leich mal mit statisc geinkten Libs).
Sind die Libs alle in einem Projekt, so dass ein "make clean" alles Kompilierte platt macht? Wenns verschiedene Projekte sind, und du kein automatisiertes Buld-System verwendest, heißt es "alle Abhängigkeiten neu bauen"...

Problem ist halt, dass das was du uns an Infos lieferst (liefern kannst) nicht ausreicht, um das Problem benennen zu können. Die Aufspaltung in Libs ist jetzt ein weiteres Detail. Vielleicht gibt es noch mehr (Client/Server-Architektur, Mischung mehrerer Eventloops wie Qt + GLib, usw). Und Raten war noch nie eine gute Hilfe bei der Fehlersuche :P
graythornAK
Beiträge: 29
Registriert: 16. März 2007 15:14

Beitrag von graythornAK »

franzf hat geschrieben:
graythornAK hat geschrieben: Vielleicht gibt es noch mehr (Client/Server-Architektur, ...
Nein, mehr Datails gibts (momentan) nicht.
Alle libraries sind aktuell, dafür sorgt ein automatische build-prozess

Ich werde mal die entsprechenden Klassen zu einem lauffähigen Program (mit gleichen Symptomen) zusammensetzen und hier einstellen. Muß dafür aber die Klassen von Ballast befreien, wie z.B. Übergabeparameter (Referenzen auf andere Klassen), etc...
graythornAK
Beiträge: 29
Registriert: 16. März 2007 15:14

Beitrag von graythornAK »

So, habe mal eine Testumgebung mit den Klassen erstellt.

Es ist eine Applikation mit 2 Menüpunkten. Nicht wundern über die Programmstruktur. So macht sie wenig Sinn, aber in der Hauptapplikation schon.
Hintergrund ist folgender. Es werden Kommandos ausgeführt, welche aufgrunf ihrer Laufzeit immer als eigenständige Threads laufen um das Hauptprogram nicht zu blockieren. Abhängig von Laufzeit, Prozessoranzahl und Auslastung des Rechners zerfällt dieser in mehrere Teilthreads zur besseren Auslastung der evtl. vorhandenen mehreren Kerne. Da soll aber vollständig vom Kommando geregelt werden.

Run1Thread:
========
Dieser Menupunkt startet einen Thread, dieser wartet 3 Sekunden und meldet dann Erfolg.


RunSubThread:
==========
Dieser startet einen MainThread. Im MainThread wird ein SubThread gestartet. Dieser SubThread wartet ebenfalls einfach 3 Sekunden

In der OnFinished-Methode ruft er eine Methode von MainThread auf. Diese decrementiert einen Counter in MainThread, worauf sich dieser ebenfalls beendet.

Das klappt jedoch nicht, da die OnThreadFinished()-Methode von SubThread nicht aufgerufen wird - wie bereits besprochen.

Ach ja, ich benutze Eclipse und MinGW, falls das jemanden interessiert.

Danke für eure Hilfe
Dateianhänge
threadTest.zip
(16.45 KiB) 196-mal heruntergeladen
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Christian und ich hatten schon recht: das geht nicht ohne Eventloop. Aber die Lösung ist mit sowieso viel einfacher:

Code: Alles auswählen

bool MainThread::OnRun()
{
   ...
   pSubThread->StartThread();
   exec();
}

void MainThread::OnSubThreadFinished()
{
  ....
  if (m_iTaskCount == 0)
    quit(); // verlasse "exec()" in der run-Methode
}
Aber ganz unter uns: Mit scheint das alles ziemlich umständlich.. denn:
- für den Referenzzaehler brauchst du gar keinen Thread (MainThread ist in diesem Beispiel überflüssig)
- die zyklomatische Abhängigkeit zwischen Main- und SubThread ist auch überflüssig, weil "OnSubThreadFinished()" ja auch über das "finished()"-Signal des SubThreads aufgerufen werden könnte, anstelle dass man es im SubThread aufruft..

hth!

[EDIT]
übrigens ist "SubThread::OnThreadFinished()" in deinem Beispiel nicht zulässig, weil du da im Kontext eines Threads GUI-Operationen durchfuehrst..arbeite lieber mit "qDebug()".
graythornAK
Beiträge: 29
Registriert: 16. März 2007 15:14

Beitrag von graythornAK »

Zunächst mal vielen Dank für eure Hilfe.

Mit Eventloop klappt's jetzt im Prinzip. Jetzt bin ich einen Schritt weiter :D

aber es bleiben noch einige Fragen offen:

1.) Warum funktioniert mein Beispiel im Falle ohne subthread, also wenn ich nur einen Thread (den main-Thread) starte. In diesem Fall habe ich ja auch keinen exec() Aufruf gemacht?

2.)
solarix hat geschrieben: übrigens ist "SubThread::OnThreadFinished()" in deinem Beispiel nicht zulässig, weil du da im Kontext eines Threads GUI-Operationen durchfuehrst..arbeite lieber mit "qDebug()".
Die OnThreadFinished() Methode wird doch durch das finished() signal getriggert, also nachdem der Thread beendet wurde? Da müßten dann Gui-Operationen doch wieder erlaubt sein, oder?

3.)
Es ist ein weiteres Problem aufgetreten, welches ich gerade untersuche. Dazu eine allgemeine Frage:

Folgendes Scenario:
-Mainthread startet viele subthreads.
-Sowohl Mainthread als auch die Subthreads arbeiten auf einem gleichen Speicherbereich der durch einen Pointer durchgereicht wird.
- Mainthread greift nur lesend auf den gesamten Speicherbereich zu.
-die subthreads greifen lesend und schreibend auf diesen Speicherbereich zu, wobei allerdings gewährleistet ist, das sie auf verschiedene Teile des Speichers zugreifen. Es gibt also keine "überlappenden" Speicherbereiche zweier subthreads auf diesen Speicherbereich.

:?: :?: :?: Ist das ein Problem? Falls ja, was müßte ich machen? :?: :?: :?:


solarix hat geschrieben: Aber ganz unter uns: Mit scheint das alles ziemlich umständlich..
[/code]

Richtig, für das Beispiel ist das ganze geradzu Unsinn. In dem Program, in dem ich das benötige aber nicht. Dieses muß bestimmte Schnittstellen bedienen und unterliegt gewissen Randbedingungen...

Noch einmal vielen Dank, ihr habt echt Ahnung :-)
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

1) Weil der eine Thread im Kontext des Hauptthreads (qApp->thread();)erstellt wurde, und deshalb dessen Eventloop verwendet. Dein SubThread wurde aber in einem Kontext eines neuen Threads erstellt (alles in run() passiert in einem eigenen Thread!), und dieser Thread hat KEINE eigene EventLoop (fehlendes exec() in MainThread::run()). Deshalb können diese Events nicht abgearbeitet werden.

2) Der SLOT wird bei einem exec() in MainThread::run() auch im Kontext dieses neuen THreads abgearbeitet, und dieser THread ist nicht gleich qApp->thread, und Guis-Sachen dürfen NUR im Hauptthread gemacht werden. Liegt daran, dass die Gui-Operationen nicht durch Mutexe geschützt sind, und deshalb bei gleichzeitigen Zugriffen Probleme auftauchen können. Zugriff heißt auch Events des WindowManagers. Dass nur ein paintEvent() aus einem neuen Thread triggern und ein MouseMoveEvent vom Hauptthread -> Dein Window-System kann ziemlich durcheinander kommen :P

3) Also nochmal: Du hast einen Speicherbereich, den sich die SubThreads teilen, jedem SubThread wird ein eigener Bereich zugeordnet. MainThread greift aber auf ALLE Speicherbereiche zu? Dann ist es klar -> Du musst Zugriffe schützen! Allerdings reicht ein QReadWriteLock, der paralleles Lesen gestattet, bei einem Schreib-Lock wird aber nur ein Zugriff gestattet.
Antworten