QThread cleaning up when application terminates
-
I have a class that receives files via TCP, the prototype:
class TcpBinaryReceiver : public QTcpServer { Q_OBJECT public: explicit TcpBinaryReceiver(QObject* pParent = nullptr); void startServer(); protected: void incomingConnection(qintptr socketDescriptor); signals: void onTcpReceived(qintptr socketDescriptor, quint32 uint32PacketNo, int intReceived); }; class ConnectionToTrainer : public QThread { Q_OBJECT private: QFile* mpobjRxFile; QTcpSocket* mpsckCli; qintptr msckDescriptor; QTimer mtmrRxEnd; quint32 muint32PacketNo; quint64 muint64Received; public: static const quint16 mscuint16RxEndTimeout; explicit ConnectionToTrainer(qintptr ID, QObject* pParent = nullptr); void run(); signals: void error(QTcpSocket::SocketError sckError); void kick(); void tcpReceived(qintptr msckDescriptor, quint32 uint32PacketNo, int intReceived); public slots: void disconnected(); void onKick(); void readyRead(); };
The implementation:
/** * @brief TcpBinaryReceiver::TcpBinaryReceiver * @param pParent Pointer to parent */ TcpBinaryReceiver::TcpBinaryReceiver(QObject* pParent) : QTcpServer(pParent) { } /** * @brief TcpBinaryReceiver::startServer */ void TcpBinaryReceiver::startServer() { quint16 uint16Port(SckServer::uint16PortTCP()); if ( !this->listen(QHostAddress::Any, uint16Port) ) { qDebug() << "Could not start TCP server!"; } else { qDebug() << "Listening to port " << uint16Port << "..."; } } /** * @brief TcpBinaryReceiver::incomingConnection * @param socketDescriptor details of new connection */ void TcpBinaryReceiver::incomingConnection(qintptr socketDescriptor) { qDebug() << socketDescriptor << " Connecting..."; ConnectionToTrainer* pThread(new ConnectionToTrainer(socketDescriptor, this)); QObject::connect(pThread, &ConnectionToTrainer::finished, [this]() { deleteLater(); } ); //pThread, &ConnectionToTrainer::deleteLater); QObject::connect(pThread, &ConnectionToTrainer::tcpReceived, this, &TcpBinaryReceiver::onTcpReceived); pThread->start(); } //Static initialisation const quint16 ConnectionToTrainer::mscuint16RxEndTimeout = 1000.0; /** * @brief ConnectionToTrainer::ConnectionToTrainer * @param ID Socket descriptor * @param pParent Pointer to parent */ ConnectionToTrainer::ConnectionToTrainer(qintptr ID, QObject* pParent) : QThread(pParent), mpobjRxFile(nullptr), mpsckCli(nullptr), msckDescriptor(ID), muint32PacketNo(0), muint64Received(0) { QObject::connect(this, &ConnectionToTrainer::kick, this, &ConnectionToTrainer::onKick); QObject::connect(&mtmrRxEnd, &QTimer::timeout, [this]() { if ( muint64Received > 0 ) { //File received DatasetTransfer* pobjTransfer(DatasetTransfer::pCurrentTransfer()); if ( pobjTransfer != nullptr ) { QFile& rFile(pobjTransfer->rFile()); emit pobjTransfer->transferComplete(rFile.fileName()); } } mtmrRxEnd.stop(); } ); } /** * @brief ConnectionToTrainer::disconnected */ void ConnectionToTrainer::disconnected() { mpsckCli->deleteLater(); } /** * @brief ConnectionToTrainer::onKick */ void ConnectionToTrainer::onKick() { mtmrRxEnd.start(ConnectionToTrainer::mscuint16RxEndTimeout); } /** * @brief ConnectionToTrainer::run */ void ConnectionToTrainer::run() { mpsckCli = new QTcpSocket(this); muint32PacketNo = 0; muint64Received = 0; //Set the ID if ( !mpsckCli->setSocketDescriptor(msckDescriptor) ) { emit error(mpsckCli->error()); return; } //Connect signals QObject::connect(mpsckCli, &QTcpSocket::connected, [this]() { qdbg() << "mpsckCli::connected!"; } ); QObject::connect(mpsckCli, &QTcpSocket::disconnected, this, &ConnectionToTrainer::disconnected); QObject::connect(mpsckCli, &QTcpSocket::readyRead, this, &ConnectionToTrainer::readyRead, Qt::DirectConnection); DatasetTransfer* pobjTransfer(DatasetTransfer::pCurrentTransfer()); if ( pobjTransfer == nullptr ) { return; } mpobjRxFile = &pobjTransfer->rFile(); //Thread loop exec(); } /** * @brief ConnectionToTrainer::readyRead */ void ConnectionToTrainer::readyRead() { Q_ASSERT_X(mpobjRxFile!=nullptr, "ConnectionToTrainer::readyRead", ConnectionToTrainer::tr("Receive file has not been connected!") .toLatin1().data()); QByteArray baData(mpsckCli->readAll()); int intReceived(baData.size()); if ( intReceived > 0 && mpobjRxFile->isOpen() == true ) { qint64 int64Written(mpobjRxFile->write(baData.data(), intReceived)); if ( intReceived != int64Written ) { return; } qdbg() << "ConnectionToTrainer::readyRead, intReceived: " << intReceived; emit tcpReceived(msckDescriptor, muint32PacketNo, intReceived); muint64Received += intReceived; muint32PacketNo++; } emit kick(); }
This works ok, the problem is when the application is closed I get a Qt critical error handler dialog, Abort, Retry, Ignore.
How can I terminate any threads when the application is closing down?
-
I have a class that receives files via TCP, the prototype:
class TcpBinaryReceiver : public QTcpServer { Q_OBJECT public: explicit TcpBinaryReceiver(QObject* pParent = nullptr); void startServer(); protected: void incomingConnection(qintptr socketDescriptor); signals: void onTcpReceived(qintptr socketDescriptor, quint32 uint32PacketNo, int intReceived); }; class ConnectionToTrainer : public QThread { Q_OBJECT private: QFile* mpobjRxFile; QTcpSocket* mpsckCli; qintptr msckDescriptor; QTimer mtmrRxEnd; quint32 muint32PacketNo; quint64 muint64Received; public: static const quint16 mscuint16RxEndTimeout; explicit ConnectionToTrainer(qintptr ID, QObject* pParent = nullptr); void run(); signals: void error(QTcpSocket::SocketError sckError); void kick(); void tcpReceived(qintptr msckDescriptor, quint32 uint32PacketNo, int intReceived); public slots: void disconnected(); void onKick(); void readyRead(); };
The implementation:
/** * @brief TcpBinaryReceiver::TcpBinaryReceiver * @param pParent Pointer to parent */ TcpBinaryReceiver::TcpBinaryReceiver(QObject* pParent) : QTcpServer(pParent) { } /** * @brief TcpBinaryReceiver::startServer */ void TcpBinaryReceiver::startServer() { quint16 uint16Port(SckServer::uint16PortTCP()); if ( !this->listen(QHostAddress::Any, uint16Port) ) { qDebug() << "Could not start TCP server!"; } else { qDebug() << "Listening to port " << uint16Port << "..."; } } /** * @brief TcpBinaryReceiver::incomingConnection * @param socketDescriptor details of new connection */ void TcpBinaryReceiver::incomingConnection(qintptr socketDescriptor) { qDebug() << socketDescriptor << " Connecting..."; ConnectionToTrainer* pThread(new ConnectionToTrainer(socketDescriptor, this)); QObject::connect(pThread, &ConnectionToTrainer::finished, [this]() { deleteLater(); } ); //pThread, &ConnectionToTrainer::deleteLater); QObject::connect(pThread, &ConnectionToTrainer::tcpReceived, this, &TcpBinaryReceiver::onTcpReceived); pThread->start(); } //Static initialisation const quint16 ConnectionToTrainer::mscuint16RxEndTimeout = 1000.0; /** * @brief ConnectionToTrainer::ConnectionToTrainer * @param ID Socket descriptor * @param pParent Pointer to parent */ ConnectionToTrainer::ConnectionToTrainer(qintptr ID, QObject* pParent) : QThread(pParent), mpobjRxFile(nullptr), mpsckCli(nullptr), msckDescriptor(ID), muint32PacketNo(0), muint64Received(0) { QObject::connect(this, &ConnectionToTrainer::kick, this, &ConnectionToTrainer::onKick); QObject::connect(&mtmrRxEnd, &QTimer::timeout, [this]() { if ( muint64Received > 0 ) { //File received DatasetTransfer* pobjTransfer(DatasetTransfer::pCurrentTransfer()); if ( pobjTransfer != nullptr ) { QFile& rFile(pobjTransfer->rFile()); emit pobjTransfer->transferComplete(rFile.fileName()); } } mtmrRxEnd.stop(); } ); } /** * @brief ConnectionToTrainer::disconnected */ void ConnectionToTrainer::disconnected() { mpsckCli->deleteLater(); } /** * @brief ConnectionToTrainer::onKick */ void ConnectionToTrainer::onKick() { mtmrRxEnd.start(ConnectionToTrainer::mscuint16RxEndTimeout); } /** * @brief ConnectionToTrainer::run */ void ConnectionToTrainer::run() { mpsckCli = new QTcpSocket(this); muint32PacketNo = 0; muint64Received = 0; //Set the ID if ( !mpsckCli->setSocketDescriptor(msckDescriptor) ) { emit error(mpsckCli->error()); return; } //Connect signals QObject::connect(mpsckCli, &QTcpSocket::connected, [this]() { qdbg() << "mpsckCli::connected!"; } ); QObject::connect(mpsckCli, &QTcpSocket::disconnected, this, &ConnectionToTrainer::disconnected); QObject::connect(mpsckCli, &QTcpSocket::readyRead, this, &ConnectionToTrainer::readyRead, Qt::DirectConnection); DatasetTransfer* pobjTransfer(DatasetTransfer::pCurrentTransfer()); if ( pobjTransfer == nullptr ) { return; } mpobjRxFile = &pobjTransfer->rFile(); //Thread loop exec(); } /** * @brief ConnectionToTrainer::readyRead */ void ConnectionToTrainer::readyRead() { Q_ASSERT_X(mpobjRxFile!=nullptr, "ConnectionToTrainer::readyRead", ConnectionToTrainer::tr("Receive file has not been connected!") .toLatin1().data()); QByteArray baData(mpsckCli->readAll()); int intReceived(baData.size()); if ( intReceived > 0 && mpobjRxFile->isOpen() == true ) { qint64 int64Written(mpobjRxFile->write(baData.data(), intReceived)); if ( intReceived != int64Written ) { return; } qdbg() << "ConnectionToTrainer::readyRead, intReceived: " << intReceived; emit tcpReceived(msckDescriptor, muint32PacketNo, intReceived); muint64Received += intReceived; muint32PacketNo++; } emit kick(); }
This works ok, the problem is when the application is closed I get a Qt critical error handler dialog, Abort, Retry, Ignore.
How can I terminate any threads when the application is closing down?
@SPlatten https://doc.qt.io/qt-5/qcoreapplication.html#aboutToQuit - connect a slot to this signal and ask all your active threads to finish.
https://doc.qt.io/qt-5/qthread.html#quit
https://doc.qt.io/qt-5/qthread.html#wait-1 -
@SPlatten https://doc.qt.io/qt-5/qcoreapplication.html#aboutToQuit - connect a slot to this signal and ask all your active threads to finish.
https://doc.qt.io/qt-5/qthread.html#quit
https://doc.qt.io/qt-5/qthread.html#wait-1 -
@jsulm , Thank you, how do you get a pointer to QCoreApplication? In main():
QApplication a(argc, argv);
Is there a way to access QCoreApplication with an API call?