QThread richtig einsetzen



  • Darf es denn nur eine Instanz davon geben? Wenn ja kannst du das als Singleton implementieren. Für mich klingt das aber nach einem Ersatz für eine globale Variablen und das ist keine gute Idee.
    Wenn du den Agilenten nur in einer Klasse benutzt, nimm doch einfach eine Member Variable. Dann hast du in der Klasse auch nur eine Instanz, aber kein Ggobales Objekt.



  • Schlangenmensch schrieb:

    Darf es denn nur eine Instanz davon geben?

    Ich bin immer noch nicht zu 100% sicher.Problem ist: wenn die Verbindung aus irgenwie einen grund unterbrochen wird, muss dann die SW neue gestartet werden um die Verbindung wieder zu herstellen.



  • Wenn es nur eine Verbindung geben darf, wäre ein Singleton eine Mögliche Lösung.

    Aber da du bisher mehrere Objekte von dem Typen benutzt, kann ich mir das nicht vorstellen, daher würde ich an deiner Stelle kein Singleton benutzen. Wie ich bereits schrieb, kannst du auch so auf nur einer Instanz der Klasse arbeiten.

    Verbindungsabbrüche musst du abfangen. Ob du jetzt ein Singleton verwendest, oder nur so eine Instanz. Wenn die Verbindung unterbrochen wird, muss die neu aufgebaut werden. Das geht aber auch ohne einen kompletten Programm Neustart.



  • Hi,

    ich bin deine Vorschlag umgesezt in dem ich von CAgilentLan einen membervariable erstellt habe:

    CAgilentLan* m_workerObject;
    

    dann habe ich der Konstruktor der Klasse, wo der Object "CAgilentLan" gebraucht folgende geschrieben:

    QThread * workerThread = new QThread();
    	workerObject = new CAgilentLan(settings_l.strfuGetIP_DSO(), 500, settings_l.bofuGetSimulate());
    	workerObject->moveToThread(workerThread);
    
    	connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
    	connect(workerThread, &QThread::finished, workerObject, &CAgilentLan::deleteLater)
    

    bei der destruktor der Class habe ich folgende:

    if(m_workerObject != NULL)
    	{
    		delete m_workerObject;// hier knallt
    	}
    

    ich bekomme folgende Fehlermeldung:

    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 9c03e8. Receiver '' (of type 'QNativeSocketEngine') was created in thread 4c3a838", file kernel\qcoreapplication.cpp, line 541
    


  • Diese Error tritt sobald die Application beendet wird.



  • Hi,

    wenn du den Thread auch im Konstruktor erstellst, würde ich daraus auch eine Member Variable machen. Du möchtest den ja hinterher mit

    workerThread -> start();
    

    aufrufen. Und das geht nur, wenn der Thread an der Stelle des aufrufes bekannt ist.

    Der Grund für die Assertion ist das du delete aufrufst, das Objekt aber inzwischen im Thread Context lebt. Außerdem hast du mit deleteLater das Objekt schon zum löschen freigegeben, nach dem finished aufgerufen wird (also the Thread beendet wurde).

    Da ich hier grade keine QT Installation zum ausprobieren habe, gibt mir Google für das Connect aber folgende Syntax:

    QThread * workerThread = new QThread();
    workerObject = new CAgilentLan(settings_l.strfuGetIP_DSO(), 500, settings_l.bofuGetSimulate());
    workerObject->moveToThread(workerThread);
    
    connect(workerObject , SIGNAL(finished()), workerObject , SLOT(deleteLater()));
    connect(workerThread , SIGNAL(finished()), workerThread , SLOT(deleteLater()));
    

    Das heißt, wenn der Thread das SIGNAL finished() schickt, werden die Objekte zum löschen freigegeben. Das übernimmt dann QT und muss nicht nochmal von Hand im Destruktor gemacht werden.
    Wenn du den Thread und den CAgilentLan in der Klasse häufiger verwenden willst, darfst du sie nicht mit dem ersten "finished" Signal löschen.



  • Schlangenmensch schrieb:

    connect(workerObject , SIGNAL(finished()), workerObject , SLOT(deleteLater()));
    connect(workerThread , SIGNAL(finished()), workerThread , SLOT(deleteLater()));
    

    Das ist noch die "alte" syntax. Mit Qt5 wurde die syntax erweitert damit man auch signale z.b. mit einem lambda verknüpfen kann:
    https://woboq.com/blog/new-signals-slots-syntax-in-qt5.html



  • Argh, my fault... das kommt davon wenn man nicht aufpasst und Google noch schnell QT 4.x Dokus raus haut.



  • Hi,

    ich muss noch mal fragen "Sorry"
    So sieht es aus die Connect Funktionen in der Konstruktor:

    connect(m_pvWorkerThread, &QThread::finished, m_pvWorkerThread, &QThread::deleteLater);
    	connect(m_pvAgilentWorker, &CAgilentLan::finished, m_pvAgilentWorker, &CAgilentLan::deleteLater);
    

    die

    m_pvWorkerThread->start();
    

    Wo soll ich die bitte aufrufen?
    Wenn ich die in der Konstruktor aufrufe dann knallt. Wenn ich die auch gar nicht aufrufe dann knallt auch aber in eine andere Stelle "Wo das Programm beendet wird".



  • Du musst das an der Stelle aufrufen, an der du möchtest, dass der Thread anfängt seine Aufgaben abzuarbeiten.

    Was für einen Fehler bekommst du denn, wenn du das im Konstruktor aufrufst? Eigentlich sollte das egal sein.
    Und wo stürzt er dann genau ab? Sicher nicht bei

    m_pvWorkerThread->start();
    

    , oder?



  • Schlangenmensch schrieb:

    Du musst das an der Stelle aufrufen, an der du möchtest, dass der Thread anfängt seine Aufgaben abzuarbeiten.

    Was für einen Fehler bekommst du denn, wenn du das im Konstruktor aufrufst? Eigentlich sollte das egal sein.
    Und wo stürzt er dann genau ab? Sicher nicht bei

    m_pvWorkerThread->start();
    

    , oder?

    An diese Stelle nicht.

    m_pvWorkerThread->start()
    

    aber bei Erzeugung das Object wo die CAgilent aufgerufen wird.
    Fehlermeldung:

    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 4c19f68. Receiver '' (of type 'QNativeSocketEngine') was created in thread 4c582e0", file kernel\qcoreapplication.cpp, line 541
    


  • Das scheint wieder am löschen des Objektes zu liegen.

    Zum einen würde ich dir empfehlen en QThread nicht mit "new" zu erstellen, sondern einfach auf dem Stack.

    Zum anderen, schau dir die Zeile nochmal an:

    connect(m_pvAgilentWorker, &CAgilentLan::finished, m_pvAgilentWorker, &CAgilentLan::deleteLater)
    

    Du willst das Objekt löschen, wenn der Thread fertig ist. Also muss welches Objekt der Sender sein?

    Außerdem, wenn du das häufiger verwenden möchtest (was ja der Ausgangspunkt war), willst du das nicht nach dem finished Signal des Threads löschen, sondern ja eventuell nochmal verwenden.



  • Hallo,
    Ich habe das Connect so angepasst:

    connect(m_pvWorkerThread, &QThread::finished, m_pvWorkerThread, &CAgilentLan::deleteLater);
    

    Beim Application Starten bekomme ich eine Fehlermeldung "MyApllication.exe has triggered a breakpoint"in folgende Codeabschnitt:

    static void deallocate(QArrayData *data)
        {
            Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
            QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy));
        }
    

    Egal wie ich das umdrehe habe ich stets die gleiche Fehlermeldung.



  • Was sind denn die letzten Zeilen Code die aufgerufen werden und die du geschrieben hast? Sollte sich aus dem Callstack ablesen lassen.



  • Hi,

    Schlangenmensch schrieb:

    Was sind denn die letzten Zeilen Code die aufgerufen werden und die du geschrieben hast? Sollte sich aus dem Callstack ablesen lassen.

    In der Konstruktur wo das Object

    CAgilentLan
    

    aufgerufen wird folgende Code geschrieben:

    m_pvWorkerThread = new QThread();
    	m_pvAgilentWorker = new CAgilentLan(settings_l.strfuGetIP_DSO(), 500, settings_l.bofuGetSimulate());
    	m_pvAgilentWorker->moveToThread(m_pvWorkerThread);
    	connect(m_pvWorkerThread, &QThread::started, m_pvAgilentWorker, &CAgilentLan::agilentStart);
    	connect(m_pvAgilentWorker, &CAgilentLan::finished, m_pvWorkerThread, &QThread::quit);
    
    	connect(m_pvWorkerThread, &QThread::finished, m_pvWorkerThread, &QThread::deleteLater);
    	connect(m_pvWorkerThread, &QThread::finished, m_pvWorkerThread, &CAgilentLan::deleteLater);
    	m_pvWorkerThread->start();
    

    und genau hier liegt der Fehler.



  • Was soll die folgende Zeile machen???

    connect(m_pvAgilentWorker, &CAgilentLan::finished, m_pvWorkerThread, &QThread::quit);
    


  • wenn des Signals der Object CAgilentLan aufgerufen wird. dann muss der Thread zu ende sein.



  • QThread::quit() beendet die Eventloop in deinem Thread, falls vorhanden. QThread::terminate() kann threads wohl beenden. Wann das genau passiert ist wohl System abhängig
    Aber willst du das wirklich? Der Agilenten ist an den Thread gekoppelt und führt die Aktion aus der Klasse aus, ich denke nicht, dass das der Weg ist, wie das funktionieren kann.

    Außerdem glaube ich nicht, dass dein Agilenten Objekt das Signal emited, oder Emittenten sollte.

    Kleiner Tipp: Bau dir erstmal in einer Sandbox ein funktionierendes Beispiel mit einem QThread. Und versuche das dann auf deinen Agilenten zu übertragen.



  • Hallo zusammen,

    @Schlangenmensch

    Problem habe ich mittlerweile gelöst.
    Es reicht eigentlich vollkommen, wenn ich Signal/Slot verwende.
    Danke noch mal.



  • Ich nutze einfach mal den Thread hier weil ich ebenfalls ein Problem mit QThread habe.

    Ich nutze QThread folgendermaßen, um aufwändige Arbeit zu erledigen:

    TimeSeriesManager::TimeSeriesManager( QObject *parent ) :
        QObject( parent )
    {
        auto worker = new TimeSeriesLoader();
        worker->moveToThread( &thread );
    
        assert( QObject::connect( this, SIGNAL( sigLoadTimeSeries(std::shared_ptr<TimeSeries>)), worker, SLOT(onLoadTimeSeries(std::shared_ptr<TimeSeries>)) ) );
        assert( QObject::connect( worker, SIGNAL(sigTimeSeriesLoaded(std::shared_ptr<TimeSeries>)), this, SLOT(onTimeSeriesLoaded(std::shared_ptr<TimeSeries>)) ) );
        assert( QObject::connect( &thread, SIGNAL(finished()), worker, SLOT(deleteLater())));
        thread.start();
    }
    
    TimeSeriesManager::~TimeSeriesManager()
    {
        thread.quit();
        thread.wait();
    }
    

    D.h. der Thread läuft dauernd und bekommt über SIGNAL/SLOT seine Arbeit zugewiesen. Beim Start der Applikation bekommt der Thread so an die 2000 Jobs übertragen.

    Nun will ich die Anwendung beenden. Das Problem ist, dass der Thread trotz Aufruf von "quit()" erst noch seine Eventloop abarbeitet, d.h. die Anwendung endet erst, wenn der Thread alle seine Jobs abgearbeitet hat, was reichlich bescheuert ist.

    Gibt es eine Möglichkeit den Thread zu überzeugen, die Eventloop zu leeren?


Anmelden zum Antworten