Solved QThread richtig einsetzen?
-
@Galilio said in QThread richtig einsetzen?:
Wenn ich folgenden habe:
Header File:
class QTcpSocket; class CAgilentLan : public QThread { public: typedef enum { enRspTimeout = -2, enRspOffline, enRspOK, enRspError } t_enRspType ; CAgilentLan(QString strIpAdress_p, int iTimeout_p, bool boSimulate_p=false, bool boDebug_p=false); ~CAgilentLan(); // High-Level Funktionen QByteArray ExecuteGetScreenshot(void); void ExecuteSaveScreenshot(QString &strPathFileName_p, const char *strFormat_p); void ExecuteSettingsSave(unsigned char ucRegisterNo_p); void ExecuteSettingsRecall(unsigned char ucRegisterNo_p); double ExecuteQuickMeasure(unsigned char ucChannel_p, char *pchType_p); double ExecuteStatMeasure(char *pchType_p, bool boTrigger_p); void ExecuteResetMeasurementStatistics(void); bool Open(const QString &strIpAdress_p, const int iTimeout_p); bool Close(const int iTimeout_p); t_enRspType Execute(const char *strCmd_p, const int iWaitResponse_p, const int iTimeout_p, const int iRspDelayed_p); QByteArray GetLastResponse(void); t_enRspType ExecuteSimulated(const char *strCmd_p); QString GetAgilentIdentifikation(); ... protected: void run(); private: bool m_boDebug; bool m_boSimulate; QMutex m_MtxProcess; QMutex m_MtxResponse; QWaitCondition m_WaitCondProcess; QByteArray m_ByteArrayWrite; QByteArray m_ByteArrayRead; QByteArray m_IDN_AgilentIdentification; //Hilfsvariable double m_SDDevPeriodeA ; double m_SDDevDCA; double m_SDDevDCB; double m_SDDevWidthZ; double m_OZiSDDevPeriodeA; double m_OZiSDDevDCA; double m_OZiSDDevDCB; double m_OZiSDDevWidthZ; double m_OZiMeanPeriodeA; double m_OZiMeanDCA; double m_OZiMeanDCB; double m_OZiMeanWidthZ; QByteArray m_ByteSTDD; QByteArray m_ByteMean; QStringList m_ListSTDDev; QStringList m_ListMean; QTcpSocket *m_pTcpSctCtrl; QString m_strCtrlIP; int m_iTimeoutClose; int m_iTimeout; int m_iResponseDelayed; int m_iWaitResponse; t_enRspType m_enRetVal; };
cpp File:
CAgilentLan::CAgilentLan(QString strIpAdress_p, int iTimeout_p, bool boSimulate_p, bool boDebug_p):m_SDDevPeriodeA(0), m_SDDevDCA(0),m_SDDevDCB(0),m_SDDevWidthZ(0) { // boSimulate_p = false; m_boDebug = boDebug_p; m_boSimulate = boSimulate_p; m_enRetVal = enRspOffline; m_strCtrlIP = QString(""); m_iTimeout = 0; m_iTimeoutClose = iTimeout_p; if (this->Execute("CreateObject", 0, 0, 0) != enRspOK) { throw(QString("Error AgilentLan.CPP/Constructor TcpSocket Create memory error")); } this->Open(strIpAdress_p, iTimeout_p); } CAgilentLan::~CAgilentLan() { while(isRunning()); this->Close(m_iTimeoutClose); while(isRunning()); if (this->Execute("DeleteObject", 0, 0, 0) != enRspOK) { throw(QString("Error AgilentLan.CPP/Destructor TcpSocket Delete memory error")); } while(isRunning()); } bool CAgilentLan::Open(const QString &strCtrlIP_p, const int iTimeout_p) { bool boRet_l = true; m_strCtrlIP = strCtrlIP_p; if (this->Execute("OpenConnection", 0, iTimeout_p, 0) != enRspOK) { throw(QString("Error AgilentLan.CPP/Open TcpSocket Open Connection error")); } if (m_boDebug) { throw(QString("Tcp Socket is open")); } return boRet_l; } bool CAgilentLan::Close(const int iTimeout_p) { bool boRet_l = true; while(isRunning()); if (this->Execute("CloseConnection", 0, iTimeout_p, 0) != enRspOK) { throw(QString("Error AgilentLan.CPP/Close Connection error")); } if (m_boDebug) { throw(QString("Tcp Socket is closed")); } while(isRunning()); return boRet_l; } CAgilentLan::t_enRspType CAgilentLan::Execute(const char *strCmd_p, const int iWaitResponse_p, const int iTimeout_p, const int iRspDelayed_p) { CAgilentLan::t_enRspType enRetVal_l = enRspOffline; if (!isRunning()) { m_ByteArrayRead.clear(); m_ByteArrayWrite.clear(); m_ByteArrayWrite.append(strCmd_p); m_iWaitResponse = iWaitResponse_p; m_iTimeout = iTimeout_p; m_iResponseDelayed = iRspDelayed_p; start(); m_MtxProcess.lock(); m_WaitCondProcess.wait(&m_MtxProcess,-1); m_MtxProcess.unlock(); enRetVal_l = this->m_enRetVal; } else { enRetVal_l = enRspError; } while(isRunning()); return enRetVal_l; } QByteArray CAgilentLan::GetLastResponse(void) { QByteArray ay_l; m_MtxResponse.lock(); ay_l = m_ByteArrayRead; m_MtxResponse.unlock(); return ay_l; } void CAgilentLan::run() { bool boRun_l = true; bool boSent_l = false; const char *pStrCmd_l = NULL; const char *pStrRsp_l = NULL; m_enRetVal = enRspError; if (strcmp(m_ByteArrayWrite.data(),"CreateObject")==0) { if (this->m_boSimulate == true) { m_enRetVal = enRspOK; } else { m_pTcpSctCtrl = (QTcpSocket *)new QTcpSocket; if (m_pTcpSctCtrl != NULL) { m_enRetVal = enRspOK; } } boRun_l = false; } if (strcmp(m_ByteArrayWrite.data(),"DeleteObject")==0) { if (this->m_boSimulate == true) { } else { if (m_pTcpSctCtrl != NULL) { delete m_pTcpSctCtrl; } } m_enRetVal = enRspOK; boRun_l = false; } if (strcmp(m_ByteArrayWrite.data(),"OpenConnection")==0) { if (this->m_boSimulate == true) { m_enRetVal = enRspOK; } else { m_pTcpSctCtrl->connectToHost(m_strCtrlIP,5025); if (m_pTcpSctCtrl->waitForConnected(m_iTimeout) == true) { m_enRetVal = enRspOK; } } boRun_l = false; } if (strcmp(m_ByteArrayWrite.data(),"CloseConnection")==0) { if (this->m_boSimulate == true) { m_enRetVal = enRspOK; } else { m_pTcpSctCtrl->disconnectFromHost(); if ((m_pTcpSctCtrl->state() == QAbstractSocket::UnconnectedState) || (m_pTcpSctCtrl->waitForDisconnected(m_iTimeout))) { m_enRetVal = enRspOK; } } boRun_l = false; } int iTimeout_l = m_iTimeout; while (boRun_l && (!this->m_boSimulate)) { if (boSent_l == false) { boSent_l = true; // Kommando absetzen m_pTcpSctCtrl->write(m_ByteArrayWrite.data(),m_ByteArrayWrite.size()); // Auf Antwort warten if (m_iWaitResponse >= 0) { QThread::msleep(m_iWaitResponse); } else { boRun_l = false; } } // Anwort auf Kommando abwarten if ((m_pTcpSctCtrl->bytesAvailable() > 0) && (boRun_l == true)) { m_MtxResponse.lock(); m_ByteArrayRead.append(m_pTcpSctCtrl->readAll()); if (m_boDebug) { throw(QString(m_ByteArrayRead.data())); } m_MtxResponse.unlock(); iTimeout_l = m_iTimeout; // iTimeout_l = m_iWaitResponse; } else { if (m_pTcpSctCtrl->waitForReadyRead(iTimeout_l) == false) { m_MtxResponse.lock(); if (m_ByteArrayRead.size() == 0) { if (m_boDebug) { throw(QString("AgilentLan Timeout")); } m_enRetVal = enRspTimeout; } else { m_enRetVal = enRspOK; } m_MtxResponse.unlock(); boRun_l = false; } } } QThread::msleep(m_iResponseDelayed); QMutexLocker locker(&m_MtxProcess); m_WaitCondProcess.wakeOne(); }
Diese Klasse möchte ich gerne umschreiben und zwar nach der vorgeschlagen "Qthread richtig einsetzen".
Das heisst ich muss eine neue Klassen erstellen, die von mir aus auch Worker heisst.#include <QObject> class WorkerClass :public QObject { public: explicit WorkerClass(QObject *parent = 0); ~WorkerClass(); public slots: void work(); };
--> Das heisst für meinen Fall alle Aufrufe der run() Methode müssen jetzt in Slot Methode work() passieren natürlich mit entsprechenden
QObject::connect()
in Konstrutor CAgilentLan(...) richtig?
Was mir wichtig bei diese Anpassung, dass der Änderung nur in diese klasse bleibt.
Ich will nicht überrall anpassen wo Objekte der klasse CAgilentLan aufgerufen sind deswegen bin ich bei der Änderung einbisschen vorsichtig.Danke
-
@Galilio
Hi, sorry für die späte Antwort, war nen langes Wochenende :-)Ok, lass mich hier klar stellen, von QThread abzuleiten ist per se nicht falsch und auch weiterhin eine legitime Möglichkeit einen Thread zu betreiben. Nicht ohne Grund war das,bis vor einiger Zeit, auch die in der Doku beschriebenen Methode.
D.h. wenn du bereits eine funktionierende Implementation hast, was ich aus deinem Codebeispiel schließe, würde ich mir nicht die Mühe machen und das umschreiben wollen, das ist nämlich nicht so einfach.
Reimplementieren von QThread/Run und Worker-Ansatzt sind vom Aufbau her unterschiedlich. Auch wenn sie im Prinzip auf das selbe zurückgreifen.
Bei QThread/Run setzt man da an, das run Zyklisch(CPU-Abhängig) aufgerufen wird, davon ausgehend Funktionen aufgerufen etc- in der Regel macht man dazu eine While-Schleife innerhalb der
RUN
da ansonsten der Thread am Ende der Funktion fertig ist.
Der Worker-Object Ansatz ist mehr Event-orientiert. Dort wird QThread, so eingesetzt wie es angedacht war, als Thread-Warpper. Dort wird dannRUN
als eigene event-loop aufgerufen, was die Objekte die man übergeben hat, am leben erhält.Zu deiner Frage:
Nein, so einfach sollte man das nicht machen.Da müsste gute Menge umgeschrieben werden.
Ganz grob:
in "Run" müsste nur TCP-Socket initialisiert werden(new und Connects), und dann der Rest ausgelagert in eigene Funktionen und zugegriffen von "außerhalb" über Signal/Slots. -
Guten Morgen und danke für die Antwort.
Grund warum ich das ändern möchte, ist beim Debug version und nur beim Visual Studio 2015 Problem habe beim dieses Class "CAgilentLan".
Grund habe ich immer noch nicht gefunden. -
@Galilio said in QThread richtig einsetzen?:
Guten Morgen und danke für die Antwort.
Grund warum ich das ändern möchte, ist beim Debug version und nur beim Visual Studio 2015 Problem habe beim dieses Class "CAgilentLan".
Grund habe ich immer noch nicht gefunden.Kannst ja mal den exakten Fehler hier posten, da kann dir gewiss jemand helfen :)
-
Problem tritt nur in Debug Version in Release ist alles okay.
Die Class "CAgilentLan" ist verantwortlich für die Oscilloscope kumminikation.
Also wird mit der Oscillocope kummniziert muss von diesem Object einen Reference erstellt werden in der Art :CAgilentLan DSO_l(settings_l.strfuGetIP_DSO(), iConstTimeoutOpenCloseDSO, settings_l.bofuGetSimulate());
diese Object ruft wieder der Konstructor und ...
dann beim verlassen der definierten Anweisung Block natürlich wird der destructor aufgerufen und hier dann kracht.
Hier ist die fehlermeldung:ASSERT: "d->active_fd.isEmpty()" in file kernel\qeventdispatcher_win.cpp, line 1248
Debug Error!ich wiederhole mich noch mal .Beim release habe ich das Problem nicht.
-
Kannst du bitte mich helfen ?
Ich komme echt gar nicht weiter und weiss es nicht was und wo ist das Problem?Danke
-
@Galilio
hi, kommt das Oszilloskop mit eigenen dll's die du einbindest?So ein crash in Debug aber nicht in Release kann damit zusammen hängen, wenn zum Beipspeil ein Object in einer dll erzeugt und von einer anderen zerstört wird. Zugewiesener Speicher sollte immer immer von dem Modul freigegeben werden der ihn auch reseverviert.
Man braucht ein wenig mehr Info, poste doch mal mehr vom Stacktrace und wenn möglich mehr vom problem-Code als eine Zeile.
-
@J.Hilk said in QThread richtig einsetzen?:
hi, kommt das Oszilloskop mit eigenen dll's die du einbindest?
Was meinst du genau?
Um die verbindung zu Oszilloskop zu ermöglichen wurde diese Class geschrieben.
Also es gibt keine andere DLL, die verwendet wurde.
Eine Sache noch: Bei Visual Studio 2008 und Qt4.8 Plugin habe ich das Problem nicht.So ein crash in Debug aber nicht in Release kann damit zusammen hängen, wenn zum Beipspeil ein Object in einer dll erzeugt und von einer anderen zerstört wird. Zugewiesener Speicher sollte immer immer von dem Modul freigegeben werden der ihn auch reseverviert.
Ich glaube dises Fall trifft mich nicht.
Man braucht ein wenig mehr Info, poste doch mal mehr vom Stacktrace und wenn möglich mehr vom problem-Code als eine Zeile
-
@J.Hilk
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.
Dir danke ich in voraus -
@Galilio ich guck mal ob ich heute Abend Zeit finde eine ausführliche Antwort zu erstellen.
-
@J.Hilk
Hi
danke dir
Eigentlich in jede Stelle wo das Object:CAgilentLan
aufegerufen wird muss ich folgende schreiben:
QThread * workerThread = new QThread(); CAgilentLan *workerObject = new CAgilentLan(.....); workerObject->moveToThread(workerThread); connect(workerThread, &QThread::started, workerObject, &CAgilentLan::agilentStart); // aufräumen connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); connect(workerThread, &QThread::finished, workerObject, &CAgilentLan::deleteLater);
Ist das so richtig?
-
@J-Hilk
noch eine Frage, da ich in das classCAgilentLan
viele exception abfänge z.B:
throw(QString("Error AgilentLan.CPP/Destructor TcpSocket Delete memory error"));
Wie soll ich solche Exception mit Signal und Slot steuern?
Danke -
Hallo,
ich habe es jetzt fast geschafft.
Ich habe in meine Application wo das Object "CAgilentLan" aufgerufen wird folgende Erweiterung
in der Konstruktor der betroffene Class als Beispiel z.B "CKlassA":m_pvWorkerThread = new QThread(); m_pvAgilentWorker = new CAgilentLan(settings_l.strfuGetIP_DSO(), 500, settings_l.bofuGetSimulate()); m_pvAgilentWorker->moveToThread(m_pvWorkerThread); //QThread und Worker aufräumen bei Programmende connect(m_pvWorkerThread, &QThread::finished, m_pvWorkerThread, &QThread::deleteLater); connect(m_pvAgilentWorker, &CAgilentLan::finished, m_pvAgilentWorker, &CAgilentLan::deleteLater);
m_pvWorkerThread und m_pvAgilentWorker sind als member Variable deklariert:
CAgilentLan* m_pvAgilentWorker; QThread* m_pvWorkerThread;
Die Frage ist wo soll ich denn:
m_pvWorkerThread -> start();
aufrufen?
Ohne diese Aufruf funktioniert aber wenn ich die Application beende knallt, da es in eine andere Stelle
eine Member Variable diese class erstelle "CKlassA" und normal in der Destruktor lösche ich das wieder und genau dort knallt.
-->Fehlermeldung:ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 478d180. Receiver '' (of type 'QNativeSocketEngine') was created in thread 47d4228", file kernel\qcoreapplication.cpp, line 541
Danke
-
@J-Hilk
Auf eine kurze Antwort werde ich mich freuen. -
@Galilio
Hi, guten Morgen,ich war leider die Woche sehr beschäftigt auf Arbeit.
Zu deiner Frage,
deine Worker-Klasse wird, so wie man es auch machen soll, im Main-Thread erstellt und anschließend in dem Thread übergeben.Das bedeutet aber auch das alle Objekte im Constructor die du ohne parent erstellst, z.B. der TCPSocket, werden nicht automatisch mit verschoben.
Also entweder alles im Worker Constructor in einer Funktion ausführen, die nachthread started
aufgerufen wird, oder die Objecte ebenfalls mit moveToThread dem QThread object übergeben. -
danke für die Antwort.
Ich brauche aber wirklich deine Hilfe.
Egal wie ich das implementiere.
Ich bekomme stets diese Fehlermeldung:ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 860030. Receiver '' (of type 'QNativeSocketEngine') was created in thread 490ef60", file kernel\qcoreapplication.cpp, line 541
Ich weiss es nicht was ich denn falsch mache...................
Vielen Dank in voraus -
Hallo,
ich bin einfach zurück zu deinem erstellten demo ThreadProjekt, die du erstellt hast.
1)
Beim Worker class hast du wie folgende implementiert:Worker::Worker(QObject *parent) : QObject(parent) { } void Worker::init() { timer = new QTimer(this); timer->setInterval(100); connect(timer, &QTimer::timeout, this, &Worker::calculate); }
Hier habe ich eine Frage im bezug auf das :
connect(timer, &QTimer::timeout, this, &Worker::calculate);
Hier tust folgende:
wird eine timeout (Signal) auftreten, dann wird das Slot calculate aufgerufen.
Richtig?
dann die restlichen Implemtierung
...void Worker::calculate() { qreal result = qPow(m_Base, m_Expo); emit qThreadResult(QString::number(result) + " um "+QTime::currentTime().toString("mm:hh:ss.zzz")); } // einfach signal "qThreadResult(...)" emittiert
2)
Bei deinem class MainWindow in der Konstruktor hast du folgende geschrieben:MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ..... //!QThread //Initalisierung des Threads und der Workerklasse QThread * workerThread = new QThread(); Worker *workerObject = new Worker(); workerObject->moveToThread(workerThread); connect(workerThread, &QThread::started, workerObject, &Worker::init); //QThread und Worker aufräumen bei Programmende connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); connect(workerThread, &QThread::finished, workerObject, &Worker::deleteLater); connect(qApp, &QApplication::aboutToQuit, workerThread, &QThread::quit); // Interaktion mit der Benutzeroberfläche connect(ui->btnQThread, &QPushButton::clicked, workerObject, &Worker::start); connect(workerObject, &Worker::qThreadResult, ui->lblResult,&QLabel::setText); connect(ui->dSpinBase, QOverload<double>::of(&QDoubleSpinBox::valueChanged), workerObject, &Worker::newBase); connect(ui->dSpinExp, QOverload<double>::of(&QDoubleSpinBox::valueChanged), workerObject, &Worker::newExponent); workerThread->start(); }
Mich interessiert das:
connect(workerThread, &QThread::started, workerObject, &Worker::init); // hier baut eine Verbindung zwischen das Signal started von QThread Object und das Slot von Worker Object //also solange der QThread noch lebt wird immer der slot init aufgerufen, der wiederum das Slot calculate aufruft.
Richtig?
10000 danke in voraus und Sorry
-
Guten Morgen @Galilio ,
zu deinem letzten post:
das Signal
QThread::started
wird nur dann ausgelöst wenn QThread bereit ist die EventLoop zu starten, also kurz bevor (intern)exec
inrun
gecalled wird.Das passiert eigendlich nur einmal in der Lebenszeit des Threads.
Ist nen Privates Signal, d.h. nur das QThread Object kann es auslösen, aber man kann sich von außerhalb an das Signal hängen, mit nem
connect
. Klick hier zum nachlesenUnd das ist auch was im Beispiel passiert. Weil
connect(workerThread, &QThread::started, workerObject, &Worker::init);
eine Qt::Queuedconnection ist, wird init auf jedenfall erst dann ausgeführt, wenn die QThread eventloop läuft.Der Connecttyp ist nicht expliciet definiert -> Auto als standart -> Queuedconnection weil Threadübergreifend.
Zu dem post davor,
sieht so aus, als ob die Objecte im Constructor im original Thread erstellst und nicht mit verschiebst. Aber ohne Code ist da schwer was zu sagen, der Stacktrace sollte dir aufschluß darüber geben in welcher Zeile das passiert. Programm mal in Debug starten und gucken :-) -
Problem tritt wenn die Application verlassen wird.
Beim Verlassen der application wird der destrctor der CAgilentLan aufgerufen, der so aussieht:
CAgilentLan::~CAgilentLan() { this->Close(GetiTimeOutClose()); if (this->Execute("DeleteObject", 0, 0, 0) != enRspOK) { throw(QString("Error AgilentLan.CPP/Destructor TcpSocket Delete memory error")); } }
die close Methode sieht wie folgende aus:
bool CAgilentLan::Close(const int iTimeout_p) { bool boRet_l = true; if (this->Execute("CloseConnection", 500, iTimeout_p, 1000) != enRspOK) { throw(QString("Error AgilentLan.CPP/Close Connection error")); } if (GetboDebug()) { throw(QString("Tcp Socket is closed")); } return boRet_l; }
und genau gesagt hier knallt:
GetTcpSocket()->disconnectFromHost();
Das Object "GetTcpSocket()" ist nicht gleich NULL, sonst hätte ich einen ganze andere Fehlermeldung.
-
Guten morgen zusammen
Problem wurde gelöst.
ich habe es immer den falschen Thread benutzt und dazu versuche ich ihm zu löschen.
danke
@J-Hilk