Search code examples
qtqthreadqtimer

How to properly stop QTimer from another thread


I see that such topic was discussed many times, but can't find clear answer to simple situation. I have Worker class running in the own thread where I create timer and want to stop it due to some condition. But I am getting the error:

Timers cannot be stopped from another thread

I fill that am missing some core logic in thread working in Qt. Can someone please explain how to resolve that? Thank you.

Here is the main.cpp

#include <QCoreApplication>
#include <QObject>
#include <QtDebug>

#include "worker.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Worker w;

    return a.exec();
}

Worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QTimer>
#include <QThread>
#include <QtDebug>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
private:
    QThread t;
    QTimer *timer;

    int count = 1;

public slots:

    void dataTimerFunction();
    void onStopTimer(QTimer *t);
signals:
    void stopTimer(QTimer *t);
};

#endif // WORKER_H

Worker.cpp

#include "worker.h"

Worker::Worker(QObject *parent) : QObject(parent)
{
    this->moveToThread(&t);
    QObject::connect(&t, &QThread::finished, this, &QObject::deleteLater);

    t.start();

    // are we in the new thread from this point, right?
    timer = new QTimer();
    QObject::connect(timer, &QTimer::timeout, this, &Worker::dataTimerFunction);
    QObject::connect(this, &Worker::stopTimer, this, &Worker::onStopTimer);
    // QObject::connect(this, &Worker::stopTimer, this, &Worker::onStopTimer, Qt::QueuedConnection);  doesn't work as well
    timer->start(200);
}

void Worker::dataTimerFunction()
{
    qDebug()<<count;
    count++;
    if (count>5){
        emit stopTimer(timer);
        //timer->stop();
    }
}

void Worker::onStopTimer(QTimer *t)
{
    t->stop();
}

Solution

  • The problem is indeed that the timer is not moved to your thread.

    Here is a modified worker class, that does what you might want to achieve. It moves the timer also to the thread and then starts and stops it by using signals / slots.

    worker.h

    #ifndef WORKER_H
    #define WORKER_H
    
    #include <QObject>
    #include <QTimer>
    #include <QThread>
    #include <QtDebug>
    
    class Worker : public QObject
    {
        Q_OBJECT
    public:
        explicit Worker(QObject *parent = nullptr);
    private:
        QThread t;
        QTimer *timer;
    
        int count = 1;
    
    signals:
        void stopTimer();
        void startTimer(int msec);
    
    public slots:
    
        void dataTimerFunction();
    };
    
    #endif // WORKER_H
    

    worker.cpp

    #include "worker.h"
    
    Worker::Worker(QObject *parent) : QObject(parent)
    {
        // are we in the new thread from this point, right?
        timer = new QTimer(nullptr);
    
        this->moveToThread(&t);
        timer->moveToThread(&t);
        QObject::connect(&t, &QThread::finished, this, &QObject::deleteLater);
        t.start();
    
        QObject::connect(timer, &QTimer::timeout, this, &Worker::dataTimerFunction);
        QObject::connect(this, &Worker::stopTimer, timer, &QTimer::stop, Qt::QueuedConnection);  
        QObject::connect(this, &Worker::startTimer, timer, static_cast<void (QTimer::*)(int)>(&QTimer::start), Qt::QueuedConnection);  
        startTimer(200);
    }
    
    
    
    void Worker::dataTimerFunction()
    {
        qDebug()<<count;
        count++;
        if (count>5){
            stopTimer();
        }
    }