Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QThread: is this the correct way to do it, and howto stop the thread and destruct the object?



  • Hi All,

    I'm tasked with writing a program that checks for something every 10ms. I have first tried to write it using QTimers in a single threaded program, but all the gui, disk-io and other processing in the GUI thread creates jitter. Therefore I am writing a program that uses the GUI thread for everything but the 10msec task, and a second thread that only runs a qTimer and the slot for the 10ms task. The 10ms task reports it's results once per second to the gui thread using a signal/slot connection.

    I have used the approach shown in this blog post: https://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/ and just above the chapter "DOs and DON'Ts" here: https://wiki.qt.io/Threads_Events_QObjects

    So in the mainwindow.cpp I create my object:
    Then I create a new thread
    Then I move my object to the new thread
    Then I use the started signal from the thread to start the timer in my object
    Finally I start the thread

    This is the first multi-threaded program I've ever written, so I would like to know if i have done it right. Also, creating the thread works fine, but what's the correct way to stop the threads when the program ends so there are no memory leaks and all the threads are guarantied to have stopped?

    Cheers,
    Cedric

    Mainwindow.h:
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include <QMainWindow>
    #include <QThread>
    #include "datacollector.h"

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow
    {
    Q_OBJECT

    public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    public slots:
    void slotData(int Value);
    private:
    Ui::MainWindow *ui;
    DataCollector *myDatacollector = nullptr;
    QThread *myThread = nullptr;
    };

    #endif // MAINWINDOW_H

    Mainwindow.cpp
    #include "mainwindow.h"
    #include "ui_mainwindow.h"

    #include <QDebug>
    #include <QThread>

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    qDebug()<<Q_FUNC_INFO << "from" << QThread::currentThread();
    ui->setupUi(this);
    myDatacollector = new DataCollector();
    connect(myDatacollector,&DataCollector::sigData,this,&MainWindow::slotData);
    myThread = new QThread;
    myDatacollector->moveToThread(myThread);
    connect(myThread,&QThread::started,myDatacollector,&DataCollector::slotStart);

    myThread->start();
    

    }

    MainWindow::~MainWindow()
    {
    qDebug()<<Q_FUNC_INFO << "from" << QThread::currentThread();
    delete ui;

    myThread->quit();
    //delete myThread;
    //delete myDatacollector;
    

    }

    void MainWindow::slotData(int Value)
    {
    qDebug()<<Q_FUNC_INFO << "from" << QThread::currentThread() << " :" << Value;
    }

    datacollector.h
    #ifndef DATACOLLECTOR_H
    #define DATACOLLECTOR_H

    #include <QObject>
    #include <QTimer>
    #include <QDateTime>

    class DataCollector : public QObject
    {
    Q_OBJECT
    public:
    explicit DataCollector(QObject *parent = nullptr);
    ~DataCollector(void);

    signals:
    void sigData(int value);
    public slots:
    void slotStart(void);
    void slotTimer(void);

    private:
    QTimer *myTimer = nullptr;
    QDateTime *myQDateTime = nullptr;
    int myValue = 0;
    int myJitter = 0;
    };

    #endif // DATACOLLECTOR_H

    datacollector.cpp
    #include "datacollector.h"
    #include <QDebug>
    #include <QThread>

    const int INTERVAL_10MS=10;

    DataCollector::DataCollector(QObject *parent) : QObject(parent)
    {
    qDebug()<<Q_FUNC_INFO << "from" << QThread::currentThread();
    myQDateTime = new QDateTime();
    }

    DataCollector::~DataCollector()
    {
    qDebug()<<Q_FUNC_INFO << "from" << QThread::currentThread();
    delete myQDateTime;
    myTimer->stop();
    delete myTimer;
    }

    void DataCollector::slotStart()
    {
    qDebug()<<Q_FUNC_INFO << "from" << QThread::currentThread();
    if (myTimer == nullptr) //cannot be done in the constructor, as the constructor runs before we move to a worker thread
    {
    myTimer = new QTimer();
    connect(myTimer,&QTimer::timeout,this,&DataCollector::slotTimer);
    myTimer->start();
    }
    }

    void DataCollector::slotTimer()
    {
    //do the stuff that has to be done real time

    //compensate for imperfect timer
    //Make sure 100 signals per second are fired
    //also, make sure signals are always fired just after the start of a second
    //so every function that waits untial some time has passed works as expected.
    const qint64 OFFSET_FROM_10MS=2;
    
    QDateTime nowdt = myQDateTime->currentDateTime();
    //qDebug()<<nowdt;
    qint64 now = nowdt.toMSecsSinceEpoch();
    
    qint64 next = (now + INTERVAL_10MS);
    next /= INTERVAL_10MS;
    next *= INTERVAL_10MS;
    next += OFFSET_FROM_10MS; //next should now be 2 msec after a 10 mc time
    
    qint64 interval = next - now;
    int jitter = static_cast<int>((next - INTERVAL_10MS) - now);
    jitter = abs(jitter);
    if (jitter > myJitter)
    {
        myJitter = jitter;
    }
    myTimer->setInterval(static_cast<int>(interval));
    myValue++;
    if (myValue >= 99)
    {
        myValue = 0;
        sigData(myJitter);
        myJitter = 0;
        qDebug()<<Q_FUNC_INFO << "from" << QThread::currentThread();
    }
    

    }


  • Lifetime Qt Champion

    Hi,

    Did you read the current QThread documentation ?

    It shows the worker object approach including object deletion.


Log in to reply