In my Qt application I create a QThread
that should perform some heavy calculation task regularly. Main QApplication
thread is supposed to maintain a GUI (not included in example) and perform some regular updates as well. Both threads have their own timers to enable regular update() calls.
Problem: When calculation workload for worker thread exceeds some critical value my main thread stops receiving timer events.
Example code is below. It outputs "Main" when update() is called for main thread, and "Worker" for worker thread. If you run it you'll see that "Worker" is printed regularly and "Main" appears exactly two times (one at the beginning, and one in about 5 seconds). In case of full-featured GUI application this would effectively mean total GUI freeze.
Some observations.
- Reducing workload by putting 100 limit on inner cycle (instead of 1000) will fix the problem (both update() methods will be called regularly).
- Setting connection type for worker thread timer signal to Qt::DirectConnection will fix the problem.
So, as you can see I have a couple of workarounds on this, but I would appreciate anybody explaining me what's the problem with the original code. I expect threads to execute their event loops independently. I know that I'm blocking the worker thread event loop by a long update() operation but why in the world does it affect the main thread?
P.S. Yes, I know about QConcurrent
alternative. But I'd just like to understand.
test.cpp
#include <windows.h>
#include <QApplication>
#include "test.h"
HANDLE mainThread_ = INVALID_HANDLE_VALUE;
QApplication *app_ = 0;
MyObj *obj_ = 0;
MyThread *thread_ = 0;
MyObj::MyObj()
: timer_(0)
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
}
void MyObj::update()
{
printf("Main\n");
}
void MyThread::run()
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
exec();
}
void MyThread::update()
{
printf("Worker\n");
// do some hard work
float f = 0.f;
for (int i=0; i < 100000; ++i)
{
for (int j=0; j < 1000; ++j)
{
f += i * j;
}
}
}
int main()
{
int argc = 0;
app_ = new QApplication(argc, 0);
obj_ = new MyObj();
thread_ = new MyThread();
thread_->start();
QApplication::exec();
return 0;
}
test.h
#include <QTimer>
#include <QThread>
class MyObj : public QObject
{
Q_OBJECT
public:
MyObj();
public slots:
void update();
private:
QTimer *timer_;
};
class MyThread : public QThread
{
Q_OBJECT
public:
void run();
public slots:
void update();
private:
QTimer *timer_;
};
UPD: I've got some answers from respectable members (read them below). Now I'd like to clarify what faulty idea broke my code in particular.
As you can see the plan was having two threads each running some update() procedure regularly. My mistake was thinking about update() as just some procedure, and it is a slot. A slot of particular object which has its own thread affinity, meaning that it's body will be executed in that thread (unless signal is dispatched with Qt::DirectConnection). Now, it appears that I've done it all right with timers -- each of them belongs to different thread -- but messed things up with update(). So I ended up executing both update() procedures in the main thread. Apparently at some point event loop becomes flooded with timer events and never finishes the iteration.
As for solutions. If you've read "You're doing it wrong" (and you should indeed) you know that it's rather convenient to have all your logic implemented in an object that is not subclassed from QThread but created separately and attached to QThread with moveToThread(). Personally I see nothing wrong with subclassing from QThread if you keep in mind that your object only controls the thread but doesn't belong to it. So it's not the place for the code you want to be executed in that thread.