PDA

View Full Version : QThread not threading??



Roman
9th November 2012, 10:06
I need a program that runs a thread with an event loop independently. Therefore I subclassed a QThread.
Thus my code looks like this;

#include "tthread.h"

TThread::TThread(QObject *parent) :
QThread(parent)
{
}


void TThread::run(){
QTimer *time = new QTimer();
time->singleShot(1000,this,SLOT(timeOUT()));
exec();
}

void TThread::timeOUT(){
while(1);
}


And my mainwindow:

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

TThread *thread =new TThread(0);
thread->start();
}


So here is what I meant to happen: My GUI mainwindow will not be affectet by the fact, that the TThread will stuck in the while(1)-loop.
But it does so my questions:
1. Why is that... what do I miss?
2. What do I have to do to really have an independent thread with an event loop?

Greetings
Roman

alizadeh91
9th November 2012, 10:51
First of all why did you use a timer inside a thread???? Your code seems messy

Roman
9th November 2012, 11:51
Well its obviously not my final programm. I need to check the time between a sent and received UDP-Message. Further if no Connection could have been established, a reconnect in a certain time should be made without action from the main application. Therefore Timers would be very usefull. Further as I posted a peeled code it doesnt look really nice I guess thats what you ment with
Your code seems messy or is there something else you dont like about my code?

alizadeh91
9th November 2012, 12:14
Instead of using extra timer and singleShot just use a sleep in thread

Roman
9th November 2012, 12:32
Well your solutions would really work... I tried it allready.... unfortunately the udpsocket cant be clodes / deleted in a thread without an eventloop further i want to implement the qtserialport (using qt5) in the same manner where there is no chance of getting it working in a thread without an eventloop...

But even apart from my application and what I want to do.... shouldn't the QThread behave like two absolutely independent programms... means freezing of one thread should not affect the other thread by any circumstances?

amleto
9th November 2012, 12:37
I need a program that runs a thread with an event loop independently. Therefore I subclassed a QThread.
Thus my code looks like this;


#include "tthread.h"

TThread::TThread(QObject *parent) :
QThread(parent)
{
}


void TThread::run(){
QTimer *time = new QTimer();
time->singleShot(1000,this,SLOT(timeOUT()));
exec();
}

void TThread::timeOUT(){
while(1);
}


And my mainwindow:


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

TThread *thread =new TThread(0);
thread->start();
}


So here is what I meant to happen: My GUI mainwindow will not be affectet by the fact, that the TThread will stuck in the while(1)-loop.
But it does so my questions:
1. Why is that... what do I miss?
2. What do I have to do to really have an independent thread with an event loop?

Greetings
Roman



void TThread::run(){
exec();
}


fixed.

Roman
9th November 2012, 12:59
void TThread::run(){
exec();
}


fixed.
What should I do whit that?
I really do the exec();

void TThread::run(){
QTimer *time = new QTimer();
time->singleShot(1000,this,SLOT(timeOUT()));
exec(); /*<-----------------------------------------
}

Talei
9th November 2012, 13:14
The reason for such behaviour is that Your QTimer lives not in new thread but in main thread.
QTimer excepts that thread even loop is running but ... You can't create QTimer before exec() because thread event loop is not running and You can't call it after exec() because code won't be executed past that point until thread is running (after QThread hit's exit() or quit() then code in run() past exec() will be executed!).

QThread is just a object that manage new thread and is not thread itself. it lives in the thread that it was created in, hance Your QTimer lives in main thread!
The run method by default (not reimplemented!) call exec().

Try this code (in Your code replace QThread subclass with QObject subclass and use QThread as shown bellow):


wThread::wThread(QObject *parent) :
QObject(parent)
{
}

void wThread::doSomeWork()
{
qDebug() << Q_FUNC_INFO;

QTimer *t = new QTimer();
t->singleShot(1000, this, SLOT(timeOut()));

}

void wThread::timeOut()
{
qDebug() << Q_FUNC_INFO;

bool doLoop = true;
int c = 0;

while (1) {
c++;
if (c > 10)
doLoop = false;

qDebug() << "in thread!";
}
}

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QThread *th = new QThread(this);

wThread *workerThread = new wThread(0);
workerThread->moveToThread(th);
th->start();
workerThread->doSomeWork(); // <-- work done in new thread!
}


Good luck.

Roman
9th November 2012, 14:33
QThread is just a object that manage new thread and is not thread itself.
Now I got it!! Thanks a lot!!

Got it all to work now. But got one more question:
Is the timer t which is initiated in the doSomeWork()-Function which is called by the mainWindow (workerThread->doSomeWork();) dependent from the MainWindow-eventloop? If so how would a structure look alike that initiate a timer right from the beginning thats in the eventloop of the new thread?

amleto
9th November 2012, 18:39
The reason for such behaviour is that Your QTimer lives not in new thread but in main thread.
QTimer excepts that thread even loop is running but ... You can't create QTimer before exec() because thread event loop is not running and You can't call it after exec() because code won't be executed past that point until thread is running (after QThread hit's exit() or quit() then code in run() past exec() will be executed!).

QThread is just a object that manage new thread and is not thread itself. it lives in the thread that it was created in, hance Your QTimer lives in main thread!




This is not correct. run() is executed in the new thread, therefore the timer is also instantiated in the new thread.

Talei
10th November 2012, 02:33
It's been a while since I touched QThread, so sorry for my mistake. My other reply is wrong at least partially.
run() and exec() are indeed already executed in new thread!. Sad thing is that I forget about this :(. I really need to stop posting after 12h+ code "marathon"...

IMHO the reason why QTimer is created within main thread is that in QTimer, actually QObject, parent thread is a thread that parent object lives in.
So QTimer's parent is TThread and parent thread for this object is main thread and not TThread itself.

That's why when You create QObject inside a QThread that object lives in TThread parent thread and not TThread itself.
And You can't do QTimer(this) inside QThread that way due to i.e.:

Example:


class wnThread : public QThread
{
Q_OBJECT

public:
wnThread(QObject *parent = 0) : QThread(parent) {
qDebug() << "wnThread ctor:" << this->currentThread();
}

void run() {
qDebug() << "in thread th b4 exec:" << this->currentThread();

exec(); // <-- enter event loop and wait until quit() or exit()

// ... code placed here will be executed -after- thread s terminated!
qDebug() << "after exec:" << this->currentThread();
}

void exec() {
qDebug() << Q_FUNC_INFO << this->currentThread();

QTimer *t = new QTimer(this);
t->singleShot(100, this, SLOT(doSomeWork()));

}

public slots:
void doSomeWork() {
int c = 0;

while (c < 1000) {
c++;
}

qDebug() << Q_FUNC_INFO << this->currentThread();
this->quit();
}

};

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
wnThread *th = new wnThread(this);
qDebug() << "main thread:" << this->thread() << "th thread:" << th->thread() << th->currentThread();
th->start();
qDebug() << "th after start:" << th->currentThread();
}


wnThread ctor: QThread(0x3e57a0)
main thread: QThread(0x3e57a0) th thread: QThread(0x3e57a0) QThread(0x3e57a0)
th after start: QThread(0x3e57a0)
in thread th b4 exec: wnThread(0x106271c8)
void wnThread::exec() wnThread(0x106271c8)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is wnThread(0x106271c8), parent's thread is QThread(0x3e57a0), current thread is wnThread(0x106271c8)
after exec: wnThread(0x106271c8)

If I'm wrong please provide example of instantiated QTmer from within a QThread subclass.

amleto
10th November 2012, 10:37
In all code previous to your post qtimer has been instantiated with no parent. So it has no knowledge at all of the 'parent' thread. It simply exists in the new thread.

Added after 33 minutes:
qtimer in a thread...


class wnThread : public QThread
{
Q_OBJECT

public:
wnThread(QObject *parent = 0) : QThread(parent) {
qDebug() << "wnThread ctor:" << this->currentThread();
}

void run() {
qDebug() << "in thread th b4 exec:" << this->currentThread();

exec(); // <-- enter event loop and wait until quit() or exit()

// ... code placed here will be executed -after- thread s terminated!
qDebug() << "after exec:" << this->currentThread();

//**************************//
// INCORRECT! The thread is still alive here!! How can it not be? run() is executed inside the thread...
}

void exec() {
qDebug() << Q_FUNC_INFO << this->currentThread();

QTimer *t = new QTimer();

qDebug() << "timer is in " << t->thread();

t->singleShot(100, this, SLOT(doSomeWork()));

}

public slots:
void doSomeWork() {
int c = 0;

while (c < 1000) {
c++;
}

qDebug() << Q_FUNC_INFO << this->currentThread();
this->quit();
}

};

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
wnThread *th = new wnThread();
qDebug() << "main thread:" << this->thread() << "th thread:" << th->thread() << th->currentThread();
th->start();
qDebug() << "th after start:" << th->currentThread();
}


don't do

QThread mynewthread = QThread(this);
It is a well known 'boo boo'.

Talei
10th November 2012, 11:59
Did You test that code?
I'm using Win with qt 4.8.1 and that QTimer don't work for me.

Slot doSomeWork() is never executed.

And why I shouldn't give parent to the QThread or to the QTimer?

ljuhrich
10th November 2012, 12:13
Note: Subclassing QThread is no more recommended by Qt, see http://qt-project.org/doc/note_revisions/5/8/view

amleto
10th November 2012, 14:20
Did You test that code?
I'm using Win with qt 4.8.1 and that QTimer don't work for me.

of course it doesn't - you didn't put an event loop in there anywhere! Additionally, the slot will only be invoked from the main thread, not the thread that run() is in!

Added after 14 minutes:




And why I shouldn't give parent to the QThread
sorry, that is fine. I was thinking of doing moveToThread(this) in a thread ctor which causes more hassle.


or to the QTimer? because that makes the qtimer associated to the main thread - but you want it in the new thread.

Added after 12 minutes:

maybe this will make things clearer.

// winthread.h


#ifndef WNTHREAD_H
#define WNTHREAD_H

#include <QThread>
#include <QTimer>
#include <QDebug>

class Shout : public QObject
{
Q_OBJECT
public slots:
void shout()
{
static int i = 0;
qDebug() << "shout " << i++ << " " << thread();
}
};

class wnThread : public QThread
{
Q_OBJECT
public:
explicit wnThread(QObject *parent = 0) : QThread(parent) {
qDebug() << "wnThread ctor:" << this->currentThread();
}

~wnThread();

void run() {
qDebug() << "in thread th b4 exec:" << this->currentThread();

QTimer t;

Shout s;

connect(&t, SIGNAL(timeout()), this, SLOT(doSomeWork())); // 'this' belongs to main thread!
connect(&t, SIGNAL(timeout()), &s, SLOT(shout())); // s belongs to this new thread

qDebug() << "timer is in " << t.thread();
t.start(3000);
exec(); // <-- enter event loop and wait until quit() or exit()

qDebug() << "after exec:" << this->currentThread();
}

public slots:
void doSomeWork() {
static int x = 0;
++x;

int c = 0;
qDebug() << "doSomeWork " << thread() << currentThread();
while (c < 1000) {
c++;
}

if (x > 5)
this->quit();
}

};

#endif // WNTHREAD_H



main


#include <QtGui/QApplication>
#include "wnthread.h"

#include <QWidget>
#include <QDebug>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
w.show();

wnThread *th = new wnThread();

th->start();

return a.exec();
}

Roman
12th November 2012, 17:58
@amleto: also Thanks for your help.
I tested your code. It was a really eyeopener to me. Thanks.
Now at first to see if I got the whole thing right i'd like to sum up:
1. QThread is not a thread itself, but handles one.
2. The QThread-Object (an thus also its functions) belongs to its creator/parent.
3. Objects in the QThread-Object belonging to the thread.

Please correct me if im wrong.

Secound: This would imply that if, for example a qudpsocket would be handled in the thread a second object is needed that really handles the socket like the code underneath? Is that right? isnt there an easier way of doing so?

#ifndef WINTHREAD_H
#define WINTHREAD_H

#include <qthread.h>
#include <qdebug.h>
#include <qtimer.h>
#include <qudpsocket.h>

class socketHdl : public QObject
{
Q_OBJECT
public:
socketHdl(QObject *parent = 0):QObject(parent){

}

public slots:
void readData(){

}
};

class wnThread : public QThread
{
Q_OBJECT
public:
explicit wnThread(QObject *parent = 0) : QThread(parent) {
qDebug() << "wnThread ctor:" << this->currentThread();
}

void run() {
qDebug() << "in thread th b4 exec:" << this->currentThread();

QTimer t;

QUdpSocket us;
socketHdl s;

connect(&t, SIGNAL(timeout()), this, SLOT(doSomeWork()));
connect(&us, SIGNAL(readyRead()), &s, SLOT(readData()));

qDebug() << "timer is in " << t.thread();
t.start(3000);
exec(); // <-- enter event loop and wait until quit() or exit()

qDebug() << "after exec:" << this->currentThread();
}

public slots:

void doSomeWork() {
static int x = 0;
++x;

int c = 0;
qDebug() << "doSomeWork " << thread() << currentThread();
while (c < 1000) {
c++;
}

if (x > 5)
this->quit();
}

};

#endif // WINTHREAD_H


thanks

amleto
12th November 2012, 18:46
1 - yes
2 - erm...
3 - If QThread object is in the main thread, then all QObject members of that instance will have to be in that thread.


your example shows you still don't quite have something right. Why have QUdpSocket us and socketHdl s ? They are both in the 'new' thread.

Perhaps this will clear things up:



class wnThread : public QThread
{

void some_func()
{
QObject obj2_;
}

QObject obj1_;
QObject* ptr_;
};


QObjects MUST be instantiated in the thread that they 'belong' to. In other words, you CANNOT pass a parent ptr into ctor that lives in another thread.
Therefore obj1_ will HAVE to live in the same thread as wnThread instance. ptr_ is not a QObject so can point to any QObject. obj2_ is only created in some_func(), so obj2_ will be in whatever thread is running some_func. This is why in your code above, t, us, & s are all in the same thread. Therefore there is no point having both s & us.

As ljuhrich pointed out, there is a method that removes all this 'nonsense' and confusion: Don't sub class QThread.



class SomeJob : public QObject
{
public:
SomeJob(QObject* parent = 0) :
obj_(parent) // This is important!! Make sure my member variables switch thread when I do
// e.g. when SomeJob.moveToThread(thread), make sure obj_ goes to the thread with me.
{
}

// some long work
void work();

// signals and slots...
// ...

// members
QObject obj_;
};

SomeWidget::SomeQuickMethod()
{
// oh no, I need to do something heavy - thread it
QThread* th = new QThread;

SomeJob* job = new SomeJob;
job->moveToThread(th);

// Now there is no confusion - all slots of job will be handled in the sub thread
// Even all slots of obj_ will be in the sub thread!

th->start();
}

Roman
12th November 2012, 22:06
Why have QUdpSocket us and socketHdl s ? They are both in the 'new' thread.

Well thats true. What I wanted to show is, that I need a secound Object (in my case the socketHdl) wich actualy reads and processes the data from the udpsocket object. As since in your code you stated, that a connect like:

connect(&t, SIGNAL(timeout()), this, SLOT(doSomeWork())); // 'this' belongs to main thread!
would result in an execution of the doSomeWork()-function in the main thread.


As ljuhrich pointed out, there is a method that removes all this 'nonsense' and confusion: Don't sub class QThread.

Thats true... so far I didnt subclass Qthread.... but in the current solution provided by ljuhrich I need to deal with two object (the th and my SomeJob). This isnt very cute since I'll have several udp-connections in the future.