QThread richtig einsetzen



  • Du musst nicht unbedingt eine neue Klasse erstellen.

    Du könntest zum Beispiel deine Klasse nicht von QThread erben lassen, sondern von QObject.
    Die run() Methode raus nehmen, bzw. umbenennen und alle Funktionsaufrufe, bei denen du QThread Funktionen aufrufst raus nehmen (bzw. umschreiben).

    Aber: Bevor du damit anfängst, überleg dir erst mal, welche Aufgabe du genau parallelisieren möchtest, warum diese Aufgabe parallel laufen soll, wie viele parallele Tasks du zulassen möchtest usw.

    Außerdem ist es dann vlt eine Überlegung wert, ob du das nicht mit Futures und Promises realisieren kannst (Ein paar Erklärungen dazu: QT: http://blog.qt.io/blog/2017/04/18/multithreaded-programming-future-promise/ Boost: https://theboostcpplibraries.com/boost.thread-futures-and-promises) Das würde dir die low level Thread Erstellung abnehmen.



  • Schlangenmensch schrieb:

    Du könntest zum Beispiel deine Klasse nicht von QThread erben lassen, sondern von QObject.

    Okay
    Das ist schon klar.

    Schlangenmensch schrieb:

    Die run() Methode raus nehmen, bzw. umbenennen und alle Funktionsaufrufe, bei denen du QThread Funktionen aufrufst raus nehmen (bzw. umschreiben).

    Die run() Methode nenne ich um zu einen SLOT Methode richtig?



  • Ja, ich glaube du meinst das richtige. Du benennst die Methode um und deklarierst die als SLOT.

    public slots:
       void doAnything();
    


  • Kannst du bitte mich auf die Sprünge helfen?
    Ich habe es so angefangen:

    class QTcpSocket;
    
    class CAgilentLan : public QObject
    {
    	Q_OBJECT
    
    public:
     ...
        CAgilentLan(QString strIpAdress_p, int iTimeout_p, bool boSimulate_p=false, bool boDebug_p=false);
        ~CAgilentLan();
    
    ...
        t_enRspType Execute(const char *strCmd_p, const int iWaitResponse_p, const int iTimeout_p, const int iRspDelayed_p);
     ..
    
    public slots:
        void AgilentStart();
    
    };
    

    Die run() Methode umbenannt und zu einen Slot deklariert.
    Mir ist dann nicht ganz klar wie soll ich die restlichen Methoden zu SLOT und SIGNAL einbinden kann?

    In der alte Version was diese Methode :

    t_enRspType Execute(const char *strCmd_p, const int iWaitResponse_p, const int iTimeout_p, const int iRspDelayed_p);
    

    Zentral.

    Das heisst jede aufruf diese Methode verusacht eine automatische Aufruf der

    run()
    

    Methode.
    Ich will weiterhin dieses Ablauf beibehalten damit die Änderung minimal bleibt.



  • Hi,

    vielleicht hilft dir die Seite: https://wiki.qt.io/QThreads_general_usage weiter.

    Kurz zusammengefasst, an der Stelle, an der du CAgilentLan erzeugst und aufrufst muss du einen QThread erzeugen, mit moveToThread das Objekt in den Thread "schieben" und dann eben die Verbindung zwischen Signal und Slot herstellen.

    Also ungetestet hast du dann irgendwo sowas

    QThread* thread = new QThread;
    CAgilentLan* worker = new CAgilentLan(/*Argumente nicht vergessen */);
    worker->moveToThread(thread);
    connect(thread, SIGNAL (started()), worker, SLOT (AgilentStart()));
    thread->start();
    

    Was hier noch fehlt ist das warten auf Beendigung des Threads (z.B. über ein Signal) und das löschen des Threads und CAgilentLan. Hierfür stellt QT für QObjects die Funktion QObject::deleteLater() bereit.



  • Hallo,

    vielen dank für die Antwort.

    Ich habe es noch eine Frage:

    Schlangenmensch schrieb:

    an der Stelle, an der du CAgilentLan erzeugst und aufrufst muss du einen QThread erzeugen....

    Diesem satz habe ich nicht genau verstanden.

    Bei jeden aufruf dieses Object CAgilentLan muss ich bei der betroffene Class in der Konstruktor sowas schreiben:

    QThread* thread = new QThread;
    CAgilentLan* worker = new CAgilentLan(/*Argumente nicht vergessen */);
    worker->moveToThread(thread);
    connect(thread, SIGNAL (started()), worker, SLOT (AgilentStart()));
    thread->start();
    

    oder verstehe ich das falsch?

    Sorry noch mal



  • An wie vielen Stellen hast du das denn?

    Jedes mal, wenn du ein Objekt der Klasse CAgilentLan in einem extra Thread ausführen möchtest, benötigst du sowas in der Richtung.

    Das muss nicht im Konstruktor sein.

    Die Frage, die sich mir nach wie vor stellt ist, was genau willst du parallel ausführen.

    Willst du mehrmals CAgilentLan::AgilentStart() parallel ausführen (wenn ich mich richtig erinnere gabs da Netzwerkverbungen)? Oder was ist deine Idee hinter der Parallelisierung.

    Edit: Zu früh um richtig zu schreiben.



  • Schlangenmensch schrieb:

    An wie vielen Stellen hast du das denn?

    Bei eine Klasse habe ich das in mehreren Stellen (15 mal).

    Jedes mal, wenn du ein Objekt der Klasse CAgilentLan in einem extra Thread ausführen möchtest, benötigst du sowas in der Richtung.

    Das muss nicht im Konstruktor sein.
    [quote="Schlangenmensch"
    Die Frage, die sich mir nach wie vor stellt ist, was genau willst du parallel ausführen.
    [/quote]
    Während es mit der Agilent kommuniziert ist, müssen die anderen Thread warten.

    Schlangenmensch schrieb:

    Willst du mehrmals CAgilentLan::AgilentStart() parallel ausführen (wenn ich mich richtig erinnere gabs da Netzwerkverbungen)? Oder was ist deine Idee hinter der Parallelisierung.

    Nein Parallesieren möchte ich das nicht.
    Mir ist wichtig, dass die Threads synchronisiert sind.

    Was ich eigentlich für die Zukunft habe, ist der jetzigen Stand der CAgilentLan class zu erweitern in dem ich die VISA "https://en.wikipedia.org/wiki/Virtual_Instrument_Software_Architecture" Standard zu benutzen.
    Grund für diese Umstieg: Der jetzigen Stand dauert sehr lange, wenn es stets eine neue Verbindug aufgebaut ist.



  • Saheb schrieb:

    Nein Parallesieren möchte ich das nicht.
    Mir ist wichtig, dass die Threads synchronisiert sind.

    Das Widerspricht sich. Natürlich kannst du mehrere Threads synchronisieren. Aber trotzdem laufen die parallel. Wenn du keine Parallelisierung haben möchtest, benötigst du keine Threads.
    Es bringt keinen Vorteil, wenn du aus deinem Hauptprogramm einen Thread startest um dann direkt im Hauptprogramm darauf zu warten, dass der Thread fertig wird.

    VISA scheint mir eine Kommunikations API zu sein. Ich habe damit noch nichts zu tun gehabt und habe auch nicht vor jetzt die Spezifikation zu lesen. Ich kann mir aber nicht vorstellen, dass die mehrere Threads erwartet.



  • Sorry
    natürlich sind mehrere Thread zusammen gestartet abgesehen von der Hauptthread "GUI".
    Ja VISA ist eine Kommunikation API.



  • An jeder Stelle wo du die Threads mit dem CAgilentLan in mehreren Threads ausführen willst, wirst du dann die Threads erstellen müssen und mit moveToThread eben den Thread an das Objekt übergeben.

    Wenn du potentiell 15 Stellen hast an denen das vokommt, dann eben an 15 verschiedenen Stellen. Eventuell musst du auch nicht immer neue Threads erzeugen, sondern kannst Threads wieder verwenden.

    Aber wenn du 15 Stellen im Code hast, wo du die Sachen ausführst, klingt das für mich so, als ob man das geschickter Designen kann. Aber das ist eine andere Frage und hat mit QThreads nichts zu tun 😉



  • Hi,

    vielen vielen dank für deine Unterstützung.

    Schlangenmensch schrieb:

    Aber wenn du 15 Stellen im Code hast, wo du die Sachen ausführst, klingt das für mich so, als ob man das geschickter Designen kann.

    Welce Design Pattern hätte man nehmen soll (nur zu Info).



  • Saheb schrieb:

    Welce Design Pattern hätte man nehmen soll (nur zu Info).

    Das kann ich nicht beantworten, ohne genauere Informationen über das gesamte Projekt zu haben. Das würde hier deutlich zu weit gehen.
    Aber an 15 verschiedenen Stellen unterschiedliche Instanzen einer recht Zentralen Klasse zu haben ist doch eher ungewöhnlich.

    Hast du viele Klassen, die einen CAgilentLan als Member haben? Dann würde sich vlt. Vererbung anbieten.
    Möglicherweise auch ein strikteres objektorientiertes Design (SOLID)... es gibt viele Möglichkeiten und die können alle richtig oder total falsch sein.



  • Schlangenmensch schrieb:

    Hast du viele Klassen, die einen CAgilentLan als Member haben?

    In noch eine Klasse wird die CAgilentLan verwendet.



  • Hallo,

    wie gesagt da ich bei eine Klasse das Objekt CAgilentLan mehr als 15 mal aufrufe und jedes mal wird eine Verbindung zu Agilent hergestellt und wieder geschlossen und beim nächsten Aufruf das gleiche wieder : Verbindung hergestellt und wieder zu und..... was auch sehr viel Zeit kostet, die für das Endtest spürbar ist.

    Meine Frage ist: Kann ich diese Aufruf Zentral machen?

    Mit dem Aufruf meine ich das hier:

    QThread* thread = new QThread;
    CAgilentLan* worker = new CAgilentLan(/*Argumente nicht vergessen */);
    worker->moveToThread(thread);
    connect(thread, SIGNAL (started()), worker, SLOT (AgilentStart()));
    thread->start();
    

    Danke



  • Das lässt sich bestimmt optimieren. Aber sowas ist schwer allgemein zu beschreiben.

    Sind das unterschiedliche Agilents zu denen du eine Verbindung aufbaust. Bestehen mehrere Verbindungen Zeitgleich?

    Wenn du immer zu dem selben Agilent eine Verbindung benötigst könntest du eine (oder je nach dem wie viele du brauchst) Instanz von CAgilentLan als Member einführen und im Konstruktor die Verbindung aufbauen und im Destruktor wieder schließen. Eventuell musst du dann aber überprüfen ob der Agilent die Verbindung abgebrochen hat, bevor du Daten überträgst.



  • Schlangenmensch schrieb:

    Das lässt sich bestimmt optimieren. Aber sowas ist schwer allgemein zu beschreiben.

    Sind das unterschiedliche Agilents zu denen du eine Verbindung aufbaust. Bestehen mehrere Verbindungen Zeitgleich?

    Ja Sie sind mehrere Agilents.
    Es wird softwaremässig validiert, um welche Agilent sich handelt mitder entsprechenden richtige Commando ja nachdem um welchen Task sich handelt (RUN,STOP Trigger......).
    Nein es ist nicht möglich mehrere Verbindung Zeitgleich.

    Schlangenmensch schrieb:

    Wenn du immer zu dem selben Agilent eine Verbindung benötigst könntest du eine (oder je nach dem wie viele du brauchst) Instanz von CAgilentLan als Member einführen und im Konstruktor die Verbindung aufbauen und im Destruktor wieder schließen.

    Eventuell musst du dann aber überprüfen ob der Agilent die Verbindung abgebrochen hat, bevor du Daten überträgst.

    Ja das mache ich auch
    Da die Agilent als Option gedacht wurde und in der GUI mit einem Flag vorgesehen.
    Das heisst: wenn einen Häkchen (Agilent Verbindung ist gefordert)gesetzt ist,wird dann dafür gesorgt dass eine Verbindung hergestellt wird.

    Meine Frage kann ich diese Aufruf der Agilent zentral erstellen, damit es nicht jedesmal eine neue verbindung aufgebaut wird.
    Idee: damit kann ich einbisschen Zeit gewinnen.



  • Idee ist eine Singleton design pattern.

    Was sagst du dazu?



  • Erklär mal, warum du meinst, dass ein Singleton dafür geeignet ist.

    Ich persönlich bin nicht der größte Freund von Singletons. Sie haben ihre Berechtigung, aber nicht überall.



  • Somit kann ich sichergehen, dass nur einen Objeckt von CAgilentLan erzeugt wird.
    Ich muss nicht jedes Mal eine neue Objeckt erzeugen.
    Mir geht es darum am meisten um den Code efiizienter zu gestalten und vorallem die Zeit der Zugriffe auf der Agilent zu reduzieren.

    Der ist Stand: z.B: Wird es 100 mal mit der Agilent kommuniziert (Im sinne von Commando verschicken Screnshot gespeichert und .....), dann wird jedes mal eine neue Verbindung aufgebaut.

    Grosse Nachteil ist, dass dieses Objeckt global verfügbar ist (Es entspricht nicht der Sinn der Object orientierte Programmierung).


Anmelden zum Antworten