Create and destroy threads with Qt

1

I'm doing tests with C ++ and Qt. I want to make a program that according to the data found in the database create X threads to make each of them a laaaargo process. This process must be repeated indefinitely while the user does not stop it.

For this you have two options: * Stop the process temporarily. Typical "start / stop". * Restart them.

Restarting them makes sense when the user has modified the data stored in the database or files (the configuration).

If I restart it I want to delete all . Eliminate dynamically created threads and objects that have been moved to these threads to perform the job. Then we would have to start the process from scratch.

I know you could tell the users to close the application and come back to execute it, but it would not be elegant. I would like to learn how to do it well.

This is the code I use for the tests:

myWorker.h

#ifndef MYWORKER_H
#define MYWORKER_H

#include <QCoreApplication>
#include <QObject>
#include <QDebug>

namespace jlu
{
    class myWorker : public QObject
    {
            Q_OBJECT

        public:
            explicit myWorker ();
            ~myWorker ();
            void setWorkerID (int workID);
        signals:
            void startProcess();
        public slots:
            void changeStatus (int id, bool newStatus);
            void process();

        private:
            void loadConfigData();

            // Propiedades de la clase:
            bool isActive;
           int myID;
    };
}


#endif // MYWORKER_H

myWorker.cpp

#include "myworker.h"

jlu::myWorker::myWorker()
{
    connect (this, SIGNAL (startProcess()),
             this, SLOT (process()));
}

jlu::myWorker::~myWorker()
{
    // Mensaje que uso para comprobar si se ha eliminado o no.
    qDebug() << "Se ha eliminado al hilo con ID: " << myID;
}

void jlu::myWorker::setWorkerID (int workID)
{
    if (0 <= workID)
    {
        myID = workID;
    }
    else
    {
        myID = 0;
    }
}

void jlu::myWorker::changeStatus (int id, bool newStatus)
{
    if (myID != id)
    {
        return;
    }

    isActive =  newStatus;

    if (isActive)
    {
        emit startProcess();
    }
}

void jlu::myWorker::process()
{
    loadConfigData(); // Carga toda la información necesaria para funcionar.
    qDebug() << "Se inicia el proceso con id = " << myID;

    while (isActive)
    {
        // Un laaargo proceso que se repita indefinidamente

        // Atendemos los eventos que se hayan producido.
        QCoreApplication::processEvents (QEventLoop::AllEvents);
    }

    qDebug() << "Proceso detenido.";
}

void jlu::myWorker::loadConfigData()
{
    // Carga datos desde la base de datos.
    // Carga datos de archivos de configuración etc.
}

manageWorkers.h

#ifndef MANAGEWORKERS_H
#define MANAGEWORKERS_H

#include <QObject>
#include <QThread>
#include <QDebug>
#include "myworker.h"

namespace jlu
{
    class manageWorkers : public QObject
    {
            Q_OBJECT
        public:
            explicit manageWorkers (QObject * parent = nullptr);
            ~manageWorkers();
            void loadAndStartAllWorkers();
        signals:
            void changeStatusOfWorker (int id, bool newStatus);
        public slots:
            void killallWorkers();
        private:
            int numWorkers;
            QThread * myThreads = NULL;
            myWorker * myProcess = NULL;
    };
}
#endif // MANAGEWORKERS_H

manageWorkers.cpp

#include "manageworkers.h"

jlu::manageWorkers::manageWorkers (QObject * parent) : QObject (parent)
{

}

jlu::manageWorkers::~manageWorkers() {}


void jlu::manageWorkers::loadAndStartAllWorkers()
{
    numWorkers = 4; // Esto es realmente el resultado de una consulta SQL

    if (0 == numWorkers)
    {
        return;
    }

    myThreads = new QThread[numWorkers];
    myProcess = new jlu::myWorker[numWorkers];

    for (int i = 0; i < numWorkers; i++)
    {
        myProcess[i].setWorkerID (i); // Se tomará de la consulta realizada
        myProcess[i].moveToThread (&myThreads[i]);

        connect (&myThreads[i], SIGNAL (finished()),
                 &myProcess[i], SLOT (deleteLater()));
        connect (this, SIGNAL (changeStatusOfWorker (int, bool)),
                 &myProcess[i], SLOT (changeStatus (int, bool)));
        // Demás conexiones necesarias

        myThreads[i].start();
        // Iniciamos el proceso.
        emit changeStatusOfWorker (i, true);
    }
}

void jlu::manageWorkers::killallWorkers()
{
    for (int i = 0; i < numWorkers; i++)
    {
        qDebug() << "Se detiene el hilo n. " << i;
        emit changeStatusOfWorker (i, false);
        myThreads[i].wait (1000);
        myThreads[i].quit();
    }

    qDebug() << "Se han detenido todos los hilos";
}

main.cpp

#include <QCoreApplication>
#include <QTimer>
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QDateTime>


#include "manageworkers.h"

#define DATETIME_FORMAT "yyyy-MM-dd HH:mm:ss"




// Programa principal solo para testear el reinicio de los objetos.
int main (int argc, char * argv[])
{
    QCoreApplication a (argc, argv);

    jlu::manageWorkers * myControl = new jlu::manageWorkers();
    myControl->loadAndStartAllWorkers();
    qDebug () << QDateTime::currentDateTime().toString (DATETIME_FORMAT);

    QTimer::singleShot (10000, myControl, SLOT (killallWorkers()));

    return a.exec();
}

In this code, what I do is manageWorkers create as many threads and "Workers" objects as necessary and initialize them. After 10 seconds of operation, only for tests and tests, I eliminate (at least that is what I am trying to do) the threads and associated "Workers" objects.

In theory when finishing a QThread the object "Worker" should be finished:

connect (&myThreads[i], SIGNAL (finished()), &myProcess[i], SLOT (deleteLater()));

But this does not work. The program breaks.

I have tried to change the order of the instructions:

 myThreads[i].wait (1000);
 myThreads[i].quit();

If I do alone:

emit changeStatusOfWorker (i, false);
myThreads[i].wait();

The process stops, but it is not deleted.

How can I solve this and delete correctly the "Workers" objects and the dynamically created threads?

    
asked by Jonathan 19.11.2018 в 12:10
source

1 answer

0

I already have it solved!

The solution is to eliminate the connection:

connect (&myThreads[i], SIGNAL (finished()),
             &myProcess[i], SLOT (deleteLater()));

And delete the myProcess objects manually in the killallWorkers method, which would be as follows:

void jlu::manageWorkers::killallWorkers()
{
    qDebug() << " keys: " << netsConfigList.keys();

    for (int i = 0; i < numWorkers; i++)
    {
        qDebug() << "Se detiene el hilo n. " << i;
        emit changeStatusOfWorker (i, false);
        myThreads[i].quit();         // <---  El orden importa!! 
        myThreads[i].wait (1000);
    }

    // Ahora sí se eliminan los objetos y se libera la memoria correctamente.
    delete []myThreads;
    delete []myProcess;

    qDebug() << " Se han eliminado todos los hilos";
}

The problem was that after doing the myThreads[i].quit() the signal that told the myProcess[i] object to be destroyed when it could ... This seems to leave the object that was in the other half thread hanging around and gave the ruling that commented.

So now I can restart the threads and their processes as many times as I want and releasing the memory as I play.

    
answered by 24.11.2018 / 18:56
source