How can I run a timer inside a QThread?
Asked Answered
J

4

5

I would like to run a timer inside a QThread. I have written some code in which I am getting some error during the run time. What am I doing wrong?

(Parent is QThread(0x1498d10), parent's thread is QThread(0x11272b0), current thread is QThread(0x1498d10)

Main .h file, *mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "mythread.h"
namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
        MyThread *myt;

    private:
        Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

Main .cpp file, mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    myt = new MyThread();
    myt->start();
    MainWindow w;
}

MainWindow::~MainWindow()
{
    delete ui;
}

Class for thread, file mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>

class MyThread:public QThread
{
    public:
        MyThread();
        void run();
        QTimer *thr;

    public slots:
        void slo();
};

#endif // MYTHREAD_H

File mythread.cpp

#include "mythread.h"

MyThread::MyThread()
{
    thr = new QTimer();
    connect(thr, SIGNAL(timeout()), this, SLOT(slo()));
}

void MyThread::run()
{
    thr->start(1000);
}

void MyThread::slo()
{
    int i, j = 0;
    i = i + j;
}
Jutta answered 23/9, 2013 at 11:47 Comment(0)
S
11

Just my humble opinion: Do not to subclass QThread anymore when you do not need to.

I think you just want to run your class in a new thread or more probably you do not want to block other tasks. Your class is not the thread itself. Subclassing basically means that your class is what you are subclassing.

In other words: Let QThread do its job and concentrate on your class to do what it should do.

Example: MyClass itself does not know anything about threads. It just do what it has to do. Incrementing a value and showing results (plus some sleep part to show how it can block other functions or GUI)

Header file

#include <QTimer>
#include <QObject>

class MyClass : public QObject
{
    Q_OBJECT
public:
    explicit MyClass(bool willSleep, QString name, QObject *parent = 0);
public slots:
    void updateCount();
private:
    QTimer *timer;
    int count;
    bool m_wantToSleep;

};

Implementation

#include "myclass.h"
#include <QDebug>

MyClass::MyClass(bool wantToSleep, QString name, QObject *parent) :
    QObject(parent)
{
    this->setObjectName(name);
    m_wantToSleep = wantToSleep;
    count = 0;
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateCount()));
    timer->start(100);
}

void MyClass::updateCount()
{
    ++count;
    qDebug() << objectName() << " count: " << count;
    if (m_wantToSleep)
        sleep(1);
}

We have code which does the job.

Now implement more threads. Its very simple (memory management, etc. is not handled to have a simple example)

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QThread>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QThread *thread1 = new QThread; // First thread
    QThread *thread2 = new QThread; // Second thread

    thread1->start();
    thread2->start();

    MyClass *myClass = new MyClass(false, "normal class");
    MyClass *mySleepClass = new MyClass(true, "sleeper class");

    // Better to implement a start slot to start the timer (not implemented)
    // connect(thread1, SIGNAL(started), myClass, SLOT(start()));
    // but this suffices, because the timer will emit the first signal after the class is moved to another thread
    //mySleepClass->moveToThread(thread1);
    //myClass->moveToThread(thread1);
}

MainWindow::~MainWindow()
{
    delete ui;
}

Now we can play with threads:

Blocking GUI (of course we do not want this)

The initial example works without using new threads. Objects are in the current thread and that's why the GUI will be blocked (since I use the sleep function in one instance).

//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);

Nonblocking GUI

We have two more threads running. Why not to use them? In the example, QThreads are already running, but they play with nothing. Let's move our instances there, to ensure the main loop, where the GUI is living will not be blocked anymore.

The magic function is moveToThread

Uncomment lines and you can see that GUI will not be blocked. Both instances are in a new thread. But then again, there is a sleep function so one should be counting faster than the other. But it is not. Because they are blocking each other. They are in one thread.

mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread1);

Results in both previous cases should be: (instances lives in the same thread and shares the same event loop, so they are blocking each other)

"normal class"  count:  1
"sleeper class"  count:  1
"normal class"  count:  2
"sleeper class"  count:  2
"normal class"  count:  3
"sleeper class"  count:  3

So move them to a separate thread

Now the GUI is not blocked; neither instances each other.

mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread2);

The results should be (and the GUI should not be blocked):

"sleeper class"  count:  1
"normal class"  count:  1
"normal class"  count:  2
"normal class"  count:  3
"normal class"  count:  4
"normal class"  count:  5

I hope it was understandable. As for me, this is a more logical approach than subclassing.

Of course you can create a QThread instance in your MyClass. It is not necessary to create it outside MyClass, I just wanted to show that you can create one thread and move more instances there.

For anyone who disagree, I just wanted to say that: MyClass is a counter with thread support sounds better than: MyClass is a thread with a counter ability :)

Septuple answered 23/9, 2013 at 13:47 Comment(1)
Re "neither instances each other": Do you mean "neither instantiates each other"?Xenocrates
B
6

Your timer does not belong to your thread. You should create it in your run() method or you should call timer->moveToThread before connecting it to slots, but after the thread was started.

Check it: MyThread belongs to your main thread. You create a timer in the constructor of MyThread - so the timer belongs to the main thread too. But you are trying to initialize and use it in the ::run method, that belongs to another thread.

Bully answered 23/9, 2013 at 11:56 Comment(0)
F
4

In order to do this, you need to have an event loop in your thread.

From QTimer's man page:

In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.

From QThread's man page:

int QThread::exec () [protected]

Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). The value returned is 0 if exit() is called via quit(). It is necessary to call this function to start event handling.

Also, you need to have Q_OBJECT in your class:

class MyThread:public QThread
{
    Q_OBJECT

And finally, as Dmitry noted, you need to create a QTimer inside your thread, so the entire cpp file should look like this:

#include "mythread.h"

MyThread::MyThread()
{
}

void MyThread::run()
{
    thr = new QTimer();
    connect(thr, SIGNAL(timeout()), this, SLOT(slo()));
    thr->start(1000);
    exec();
}

void MyThread::slo()
{
    int i = 0, j=0;
    i = i + j;
}

Also, read this document.

Forwarding answered 23/9, 2013 at 11:51 Comment(1)
@user2617519 Note that using this method, your slot will be executed in the main thread, not in the thread you created. Using slots in a QThread subclass is considered wrong. From the docs: It is important to remember that a QThread object usually lives in the thread where it was created, not in the thread that it manages. This oft-overlooked detail means that a QThread's slots will be executed in the context of its home thread, not in the context of the thread it is managing. For this reason, implementing new slots in a QThread subclass is error-prone and discouraged.Centrifugal
T
0

I was able to create a simple example that starts a timer within another thread, using lambda functions. Here is the code:

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


int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);
    QThread* thread = new QThread(&app);
    QObject::connect(thread, &QThread::started, [=]()
    {
        qInfo() << "Thread started";
        QTimer* timer1 = new QTimer(thread);
        timer1->setInterval(100);
        QObject::connect(timer1, &QTimer::timeout, [=]()
        {
            qInfo() << "Timer1 " << QThread::currentThreadId();
        });
        timer1->start();
    });
    thread->start();

    QTimer timer2(&app);
    QObject::connect(&timer2, &QTimer::timeout, [=]()
    {
        qInfo() << "Timer2 " << QThread::currentThreadId();
    });
    timer2.setInterval(100);
    timer2.start();

    return app.exec();
}
Thiazine answered 23/4, 2018 at 14:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.