Seite 1 von 1

Probleme mit QEventloop - Es wird nicht verlassen.

Verfasst: 8. Juli 2008 11:20
von marwar_cottbus
Hallo!

Ich will eine berechnung auf mehrere Threads verteilen, aber irgendwie bleibt mein Programm nach der ersten Iteration in der Eventloop hängen.

Gedacht ist es so:
Ich habe eine QCoreApplication (keine GUI, daher gibt es mit QApplication Runtimefehler) die mehrere QThreads startet. Anschließend soll sie warten, bis die Threads fertig sind. Die Threads brauchen eigentlich für Ihre Funktion kein Eventloop, sie rechnen und enden dann. Wenn alle fertig sind soll sich die QCoreApplication beenden. Soweit der Plan, die Realität sieht anders aus. :(

Ich habe im Header der QCoreApplication-Klasse einen Slot definiert:

Code: Alles auswählen

class bewert_verteiler: public QCoreApplication{
	Q_OBJECT
		protected slots:
			void quit_check(){
				int warten=0;
				for(int lauf =0;lauf<thread_anz;lauf++){
					if(thread_handels[lauf]->isFinished()==false){ //hier bleibt er ohne Eventloop der Threads hängen
						warten=1;
					}
				}
				if (warten==0){
					for (int lauf =0;lauf<thread_anz;lauf++){
						thread_handels[lauf]->quit();
					}
					this->exit();
				}
			}

			
public:
...
};
Dieser Slot wird im Konstruktor und in der verknüpft:

Code: Alles auswählen

connect(thread_handels[lauf],SIGNAL(finished()),this,SLOT(quit_check()),Qt::DirectConnection);
Später wird dann aus dem Hauptprogramm eine Funktion aufgerufen, die erst die Threads und dann den Eventloop QCoreApplication startet.

Wenn die Threads kein exec() in ihrer run() besitzen kriege ich nach der Berechnung eine "QMutex::lock: Deadlock detected in Thread <Handlenr.>" Meldung in der oben markierten Zeile. Er hat ein Problem mit der isFinished Abfrage, obwohl ich im Debugger sehen kann, das die interne finished Variable des QObjects des QThreads true ist.

Ich habe das ganze auch mit Eventloops für die Threads ausprobiert:

Code: Alles auswählen

void sub_bewert_st::run(){
	//connect( this, SIGNAL(finished()),this, SLOT(deleteLater()), Qt::QueuedConnection);
	bewert_start();
	//connect(this,SIGNAL(started()),this,SLOT(quit()),Qt::DirectConnection);
	//this->setTerminationEnabled(true);
	//connect(this,SIGNAL(started()),this,SLOT(deleteLater()),Qt::QueuedConnection);
	//exec();
}
Er kommt bis zum exec() und hängt dann ohne Meldung im Eventloop. (Der Code ist auskommentiert, da es nicht funktioniert hat.)

Was mache ich falsch?

Verfasst: 8. Juli 2008 12:14
von marwar_cottbus
So, ich habe einen kleinen Workaround realisiert, indem ich eine static int zum zählen der finished-Signale verwende.

jetzt habe ich ein anderes Problem: Ich würde gerne die QCoreApplication mitsamt Qthreads mehrfach starten und beenden (ist eine Iteration). Da jede Menge andere Daten kopiert werden müssen würde ich die Initialisierung nur ungern jedesmal machen.

geht das?

Verfasst: 8. Juli 2008 13:12
von Sephral
Hallo,

ich denke du brauchst einfach noch eine weitere Instanz dazwischen, dann wird das alles etwas einfacher.
Schreibe Dir nen kleinen Controller, der die Daten und Threads verwaltet.

Ich würde es so angehen:
* Standard QCoreApplication verwenden
* Controller im main() erzeugen und dann normal in den eventloop laufen mit der QCoreApplication.
* Der Controller sammelt die Daten ein und verteilt die Daten an die Threads. Wenn ein Thread fertig ist, bekommt er neue Daten von Controller zugewiesen.

Ich denke die neuen Threading-Funktionalitäten in Qt 4.4 könnten auch für Dich interessant sein.


Ciao,
Sephral

Verfasst: 9. Juli 2008 12:47
von marwar_cottbus
Kannst Du mir irgendeine gute Beschreibung von QThreadPool nennen?

Wenn ich das richtig verstehe, werden die QThreads automatisch gelöscht und dafür schneller wieder erzeugt. Bringt das wirklich eine Verbesserung im Vergleich dazu, die Threads nicht zu löschen und sie einfach neu zu starten?

Im Augenblick habe ich eine lauffähige Version, die allerdings unbrauchbar ist. Sie ist durch das Parallelisieren langsamer geworden. :? Ich kann nicht ganz nachvollziehen weshalb, aber es ist nunmal so. Ich verwende keinen Mutex, demnach kann es nicht der Variablenzugriff sein, der die Verzögerung bringt. Inzwischen lädt jeder Thread mit QLibrary eine eigene Instanz jeder benötigten DLL. Damit sollten die DLL-Zugriffe ausscheiden. Die Anzahl der Threads ist die Anzahl der Kerne der CPU. Woran kann es noch liegen?

Verfasst: 9. Juli 2008 12:57
von Sephral
Hallo,

http://doc.trolltech.com/main-snapshot/qthreadpool.html

Ob es sich wirklich lohnt die Threads zu wiederzuverwenden statt sie einfach neu zu erzeugen, hängt letztendlich davon ab wie aufwändig Deine Berechnungen sind. Je schneller die Berechnung, desto mehr fällt das neue Erzeugen ins Gewicht.

Du könntest spaßeshalber auch mal mit der Priorität Deiner Threads experimentieren. Vielleicht bringt das auch schon etwas (QThread::HighPriority).

Ciao,
Sephral

Verfasst: 9. Juli 2008 16:14
von marwar_cottbus
Sephral hat geschrieben:Hallo,

http://doc.trolltech.com/main-snapshot/qthreadpool.html

Ob es sich wirklich lohnt die Threads zu wiederzuverwenden statt sie einfach neu zu erzeugen, hängt letztendlich davon ab wie aufwändig Deine Berechnungen sind. Je schneller die Berechnung, desto mehr fällt das neue Erzeugen ins Gewicht.
Ist die Berechnung denn schneller wenn ich die Threads neu erzeuge? Das würde schon was bringen, da an der Stelle >90% der CPU-Zeit draufgehen. (Ich habe die Funktionen nicht entworfen und möchte möglichst wenig ändern, da ich sie nicht in allen Punkten nachvollziehen kann.)
Sephral hat geschrieben: Du könntest spaßeshalber auch mal mit der Priorität Deiner Threads experimentieren. Vielleicht bringt das auch schon etwas (QThread::HighPriority).

Ciao,
Sephral
Das probiere ich mal, ich melde mich entweder in einer Stunde oder morgen wieder...

Danke!

Verfasst: 9. Juli 2008 16:56
von RHBaum
Ob es sich wirklich lohnt die Threads zu wiederzuverwenden statt sie einfach neu zu erzeugen, hängt letztendlich davon ab wie aufwändig Deine Berechnungen sind.
und haengt auch vom BS ab ... windows ist bei threads bissi besser optimiert als linux ...
Inzwischen lädt jeder Thread mit QLibrary eine eigene Instanz jeder benötigten DLL. Damit sollten die DLL-Zugriffe ausscheiden.
lies mal die winapi doku zu loadlibrary .... eine dll "mehrfach" laden in ein und dem selben prozess sollte ned moeglich sein ... du bekommst immer das selbe HInstance geliefert ...

wenn du globale variablen in der dll verwendest, iss das dann genau so wie mit gloablen variablen in deinem hauptprogramm, so rein threadtechnisch ...

Ciao ...

Verfasst: 10. Juli 2008 08:11
von Ginsengelf
Moin,
Ist die Berechnung denn schneller wenn ich die Threads neu erzeuge?
Nein, die Berechnung an sich wird nicht schneller. Einen neuen Thread zu erstellen ist aber eine recht aufwendige Operation. Wenn daher deine Berechnung im Verhältnis dazu kurz ist, lohnt es sich, einen existierenden Thread wiederzuverwenden, um ein besseres Verhältnis von Gesamtzeit zu effektiver Rechenzeit zu erhalten.

Ginsengelf

nee, die Priorität war's nicht

Verfasst: 10. Juli 2008 12:30
von marwar_cottbus
Was das wiederverwenden angeht, so arbeiten meine Threads mit Pointern auf einen bestimmten gleichbleibenden Speicherbereich. Außerhalb des Threads werden dessen Ergebnisse ausgelesen, verarbeitet und der Inhalt des Speicherbereichs verändert. Der Thread kann 1 zu 1 neu gestartet werden, also (?fast?) überhaupt kein Aufwand.


Ich hatte mich nach dem Hochstufen der Priorität zwar erst gefreut, weil der Processexplorer mir mehr Schreizugriffe in seinem Fenster anzeigte, aber das lag nur dran, dass der Processexplorer ausgebremst wurde. Die Zeitangaben der Zwischenergebnisdateien liegen nach wie vor >8 Minuten auseinander. Die Version ohne QT, die nur auf einem von 2 Kernen arbeitet schafft das in etwas über einer Minute auf einem baugleichen Rechner. Irgendwas läuft gravierend falsch. Laut Processexplorer hat mein Programm >99% der CPU-Zeit für sich allein. Ich verwende keinen Mutex-Operator, da sich die Threads nicht ins Gehege kommen können. Ich denke die größte Unsicherheit sind die externen DLLs. Diese werden zwar für jeden Thread separat geladen, aber wenn es dumm läuft ist es vielleicht doch nur eine Instanz. Das müßte sich eigentlich debuggen lassen...

Richtig geraten, die DLL wurde nur einmal geladen. Die Speicheradressen sind gleich. Mal sehen, ob sich das ändern läßt. Ich vermute, dass der Großteil der 8 Minuten dafür drauf geht, dass sich die Threads um die DLL streiten.

Der Pointer auf die QLibrary Konstrukte ist noch verschieden, aber der Speicherbereich in den die DLL geladen wurde ist in beiden Fällen gleich. Sie wurde also nur einmal geladen.