Seite 1 von 1

Diese damn ConnectionTypes in Multithreading-Umgebung

Verfasst: 29. April 2009 01:43
von cooky1976
Hallo,

ich versuche mal mein Problem zu schildern. Ich habe eine Multithreading-Umgebung.

Code: Alles auswählen

- main.cpp
  - Controller-Klasse
    - MainWindow
    - HLAThread-Klasse
      - Federate-Klasse
        - Ambassador-Klasse (Fedamb)
Man möge mir hier die Code-Tags verzeihen, aber ohne konnte ich nicht so schachteln.Die Ebenen geben gleichzeitig die Aufrufebenen an.

Folgende Klassen laufen in einem eigenen Thread
- MainWindow
- HLAThread
- Federate
- Ambassador (meines Erachtens, sind Fremdsourcen)

Ich muss nun ein Ereignis von Fedamb nach MainWindow bekommen. Nur die kennen sich nicht, wegen InformationHiding und MVC, also habe ich ein Signal von Fedamb nach Federate in Federate connected, dies ruft wiederum ein Signal auf, das im HLAThread wiederum ein Signal aufruft (connected im HLAThread.
Dieses Signal im HLAThread ist im Controller mit einem Slot im Mainwindow connected.
Alle beteiligten Klassen erben von QObject. Bis zum HLAThread wird das Signal mit einer DirectConnection weitergereicht.
Im MainWindow habe ich allerdings einen QWTPlot. Und dieser Plot erwartet in der Methode replot, dass die aus demselben Thread aufgerufen wird in dem der Plot auch läuft. Also geht hier meine bevorzugte Qt.:DirectConnection nicht. Meines Erachtens wäre hier das sinnvollste das Qt::BlockingQueuedConnection. Wenn ich das allerdings einbaue, wird der Slot in QMainwindow nie erreicht.

Ich bin schon am Überlegen, ob ich einfach die Daten meines Plots aktualisiere und dann mit dem Timer das Replot() aufrufe, aber das ist halt nicht wirklich schön, sollte abr funktionieren.

Aber kann mir jemand erklären, warum mein

Code: Alles auswählen

    connect(&threadHLA, SIGNAL(newObjectAttributeValue(const QString &, const QString &, double &)), 
        mainWin, SLOT(newObjectAttributeValue(const QString &, const QString &, double &)), Qt::BlockingQueuedConnection);
nicht ausgeführt wird?

Die Threads sollten doch nach einem Round-Robin-Prinzip oder nach einem ähnlichen Prinzip alle mal an die Reihe kommen und abgearbeitet werden. Oder kann das eventuell sein, dass mein Programm meine Variablenreferenzen vergisst?

Über jegliche Art an Aufklärung wäre ich enorm dankbar.

Ciao

Alexander

PS: Doku zu Multithreading, QWTPlot und Qt::ConnectionType habe ich mehrmals durchgearebitet.

Verfasst: 29. April 2009 08:09
von Christian81
Ich würde erstmal eine Queued-Connection probieren. Bringt connect() auch true zurück?

Verfasst: 29. April 2009 08:34
von solarix
Eine DirectConnection von Thread zu Thread bewirkt doch, dass der Slot im Kontext des emittierenden Threads aufgerufen wird. Das (Blocked)Signal von HLAThread wird also aus dem Kontext von "Federate" emittiert.
Keine Ahnung wie sich dann das System verhält.

Ich würde in deinem Fall (wie Chr. schon sagte) bei allen(!) Connections "QueuedConnections" nehmen.

Verfasst: 29. April 2009 12:46
von RHBaum
Vielleicht wird auch seine eventqueue nie abgearbeitet ... weil sie von irgend was modalem blockiert wird.

Aber zuerst Christian81's Tips beachten ... und sich auch mal die ausgabe (console, debug fenster) waehrend des connectens anschauen ....

Ciao ...

Verfasst: 30. April 2009 03:17
von cooky1976
@Christian81: Tut mir leid, ich habe natürlich mal wieder nicht geschrieben, dass connect true zurückgibt, das hatte ich schon ausprobiert.

Leider haben mir die QueuedConnections schon in der Vergangenheit kein Glück gebracht, will heissen, die SLOTS wurden nie aufgerufen. Der Grund hierfür ist mir immer noch rätselhaft.

Die Vermutung, dass die Eventqueue nie abgearbeitet wird, liegt auch meines Erachtens nahe. Ich habe aber da nirgends eine Methode zur Überprüfung gefunden. Signale werden nicht geblockt. Das habe ich bereits geprüft.
Meine Frage wäre nun, ob ich irgendwie testen kann, ob die Event-Queue abgearbeitet wird.

Zweite Frage ... wenn ich eine QueuedConnection von einem Thread in denselben habe ... was passiert dann? Meines Erachtens wird dann der Slot in dem nächsten Loop der Eventqueue aufgerufen.

Ich gebe hier noch die Headerdatei zu der Klasse Federate an. Vielleicht habe ich ja da einen für mich nicht nachvollziehbaren Bockmist gebaut:

Code: Alles auswählen

#ifndef REMOCOFEDERATE_H_
#define REMOCOFEDERATE_H_

#include "ReMoCoFedAmb.h"
#include "RTI.hh"

#include <QObject>
#include <QString>
#include <QHash>
#include <QMap>

#include "InteractionHandleValuePairSet.h"

#define READY_TO_RUN "ReadyToRun"

class ReMoCoHLAHandle;

class ReMoCoFederate : public QObject
{
    Q_OBJECT

    signals: 
        void parseAvailableScenarios(const QString &xml );
        void newAttributeValue(const QString &attributeName, const QString &value, double &time);

	public:
        	// public methods //
	        ReMoCoFederate();
        	virtual ~ReMoCoFederate();
        	void createAmbassador();
	        void createFederation(QString federationName, QString fedFilename);
	        void joinFederationWFederate(QString remocoName, QString simulatorName);
        	void initializeObjectHandles();
        	void initializeInteractionHandles();
        	void announceSyncPoint();
        	void enableTimePolicy();
        	void publishAndSubscribe();
        	void resignFederationExecution();
        	void destroyFederationExecution();
        	void cleanUpFederate();
        	void runFederate();
        	void setAsynchronousDelivery();
        	void setSynchronousDelivery();
        	void tick();
        	void connectSignalsFedAmp();
        	void disconnectSignalsFedAmp();
        	bool isReadyToRun();
        	double getFedAmbTime();
        	void advanceTime( double timestep );
        	bool getSyncPointAchieved();
        	void sendIAStartSimulation();
        	void processReceivedQueuedInteraction();

        	//interactions functions
        	void requestScenarios();
        	void sendQueuedInteraction();

    public slots:
        void scenarioChoosen(const QString &);
        void startSimulatorWithScenario();

    private slots:
        void interactionReceived(RTI::InteractionClassHandle &theInteraction,
                                 const RTI::ParameterHandleValuePairSet &theParameters);
        void attributeUpdateReceived(RTI::ObjectHandle &theObject,
                                     const RTI::AttributeHandleValuePairSet &theAttributes,
                                     double &theTime);

	private:
		RTI::RTIambassador *rtiamb;
		ReMoCoFedAmb       *fedamb;

		// variables
		QByteArray simulatorName, remocoName, federationName, fedFilename;
        
        ReMoCoHLAHandle *objectMap;
        QHash<QString, ReMoCoHLAHandle *> *interactionMap;

        RTI::FederateHandle fedHandle;

		//map for interactions
        QMap<unsigned long, InteractionHandleValuePairSet *> *interactionSentQueue;
        QMap<unsigned long, InteractionHandleValuePairSet *> *interactionReceivedQueue;

        // functions
	void updateAttributeValues( RTI::ObjectHandle objectHandle );
	void sendInteraction();
	double getLbts();
        void setSyncPointAchieved();

        QString getParameterValue(const RTI::ParameterHandleValuePairSet &theParameters, RTI::ULong index);
        QString getAttributeValue(const RTI::AttributeHandleValuePairSet &theAttributes, RTI::ULong index);

        volatile bool defineSyncPointAchieved;
};

#endif /*REMOCOFEDERATE_H_*/
Noch eine letzte Vermutung meinerseits: Könnte es sein, dass die Referenz auf meinen QString bei der Übergabe von einem Connect zum nächsten Connect irgendwie auf Grund von Lokalität flöten geht? Ich gebe ja die Referenz auf meinen QString über 2 connects weiter, da ich eben keinen direkten "Draht" von Klasse MainWindow zu Klasse Federate habe.

Für die ersten Tipps schon mal aller besten Dank.

Alexander

Verfasst: 30. April 2009 09:35
von solarix
Zweite Frage ... wenn ich eine QueuedConnection von einem Thread in denselben habe ... was passiert dann? Meines Erachtens wird dann der Slot in dem nächsten Loop der Eventqueue aufgerufen.
Genau...
Noch eine letzte Vermutung meinerseits: Könnte es sein, dass die Referenz auf meinen QString bei der Übergabe von einem Connect zum nächsten Connect irgendwie auf Grund von Lokalität flöten geht?
Nein... Sobald der Slot nicht aufgerufen werden kann/muss (QueuedConnection) wird intern einfach eine Kopie der übergebenen Instanz erstellt.

Code: Alles auswählen

class ReMoCoFederate : public QObject 
Ich dachte bisher, das sei ein THREAD! Wo erstellst du diese Instanz? Nicht zufälligerweise im GUI-Thread .. oder?
Leider haben mir die QueuedConnections schon in der Vergangenheit kein Glück gebracht
Die funktionieren tadellos. Am besten schreibst du dir zuerst mal eine kleine Spielwiese (1-2 Threads + Empfängerobjekt im Hauptthread) und übst damit... ist viel einfacher als gleich im Hauptprojekt :wink:

Verfasst: 30. April 2009 11:31
von cooky1976
Noch eine kleine "dumme" Frage .... gibt es eine Möglichkeit heruaszubekommen, wieviele Threads ich am Laufen habe? Es wird n#mlich auch noch Fremsoftware in dem Projekt verwendet, die meines Erachtens auch noch mal als Thread ausgeführt wird. Reicht da ein einfaches

Code: Alles auswählen

qDebug(thread()->currentThreadId());
reichen?

Ansonsten versuche ich wirklich mal aufzudröseln, wieviele Threads hier bei mir existieren, neben dem Hauptthread und dem Nebenthread, den ich selbst definiert habe.[/quote][/code]

Verfasst: 30. April 2009 18:04
von cooky1976
Ich bin mal ein Stück weiter ... ich lasse mir aktuell immer die ThreadID ausgeben.
Meine Kommunikationskette fängt bei MainWindow an, da hat der Thread die ID 1988, hier wird das Signal emittiert, das im HLAThread den SLOT choosenScenario aufruft. Hier ist die ThreadID immernoch dieselbe.
Ich war eigentlich der Meinung, dass, wenn ich in der Thread-Klasse eine Funktion bzw. Slot aufrufe, die Funktion im Kontext des Threads ausgeführt wird, also mit einer anderen Thread-ID. Aber hier habe ich mich also getäuscht.

Von diesem Slot, der ja noch im selben Thread ist, wie mein MainWindow, will ich nun ein Signal emittieren, dass per Connect mit einem SLOT in einem Objekt meiner run-Methode meines Threads verbunden ist:

Code: Alles auswählen

void ReMoCoHLAThread::run(){
    ReMoCoFederate *federate = new ReMoCoFederate();
...
    connect(this, SIGNAL(scenarioChoosen(const QString &)), federate, SLOT( scenarioChoosen(const QString &)));
...}
Spätestens hier aber benötige ich ja eine QueuedConnection, da ich vom SIGNAL in der Thread-Klasse, der noch zum Hauptthread gehört, zu einem SLOT in einem Child der run-Methode(eigener Thread) wechseln will. Allerdings wird hierbei scheinbar nicht die EventQueue ausgeführt.

Nun stellt sich mir die Frage ... wird denn überhaupt die EventQueue ausgeführt, wenn ich "nur" in der Run-Methode bin, oder kann ich das Ausführen der Eventqueue irgendwie antriggern?

Die komplette andere Signal-Kommunikation funktioniert ohne Probleme ... ich nehme nur an, dass es hier irgendwie am Abarbeiten der Eventqueue hängt und warum er das nicht macht, ist hier meines Erachtens die Frage.

Verfasst: 30. April 2009 19:27
von franzf
Startest du denn die EventQueue deines Threads denn überhaupt?
Also exec() in run()?

Verfasst: 2. Mai 2009 00:45
von cooky1976
So ... irgendwie stehe ich komplett auf dem Schlauch .... momentan.

Ja, ok, das mit dem exec() habe ich aus welchem Grund auch immer übersehen. Ich möchte natürlich eine EventLoop in dem Thread, da ich nicht nur vom Thread aus mit dem Hauptthread kommmuniziere, sondern auch umgekehrt.
Also steht jetzt in meiner run() nur das exec() drin, da ja das Programm aus der exec()-Funktion nur mit Beendigung des Threads wieder herausspringt.

Mein großes Problem ist ... ich habe eine Schleife, die immer im Mainthread ausgeführt werden soll, die aber nicht periodisch definiert werden kann, da die einzelne Laufzeit von externen Quellen bestimmt wird.

Wenn ich jetzt über eine Signal-Slot-Verbindung von außen die Funktion process() aufrufe (welche die Schleife abarbeitet), wird diese als Event in die Queue eingefügt und abgearbeitet, wobei dieses Event so an sich nie zu einem Ende kommt, wenn dieses Event nämlich endet, wird der Thread auch terminiert.

Nachdem aber aus meinem Verständnis nicht mehrere Events in einer Event-Queue nach dem Round-Robin-Prinzip abgearbeitet werden, blockiert meine Schleife wieder alle nachfolgenden Events. Ich bin also nicht weitergekommen, wie am Anfang.

Ergänzend muss ich noch sagen, dass in meiner Schleife ein Objekt erzeut wird, das nur in der Schleife existiert und das ich eigentlich auch nicht als Klassenmember definieren wollte.

Sieht jemand eine Möglichkeit ohne meine Variable als Klassenmember zu definieren, die Schleife im Thread abarbeiten zu lassen und gleichzeitig noch andere Events zuzulassen? Sonst muss ich eben mein Objekt (namens federate) als Klassenmember definieren und als volatile. Und dann mit 2 Threadinstanzen arbeiten.

Außer es hat dann doch einer eine geniale Idee.

Danke schon mal

Alexander

PS: So etwas ähnliches wie processEvents() gibt es ja in einem QThread laut Docu nicht, so dass ich die Queue-Abarbeitung mal anschupsen könnte und warten könnte, bis die Queue außer dem aufrufenden Event wieder leer wäre ....

Verfasst: 2. Mai 2009 10:32
von cooky1976
OK, nach einer Nacht drüber schlafen, mache ich es wieder so, wie ich es vorher gemacht hatte, ich übergebe die Events an meinen Thread mit DirectConnection und queue die dort selber um eben diese Queue zwischendrin abzufragen und abzuarbeiten.
Für das weitere Problem, das ich hatte. habe ich aber jetzt dank' der diversen Denkanstösse doch eventuell eine Lösung.