PDA

View Full Version : QThread and QEventLoop - Idle Processing



kloffy
22nd February 2008, 01:21
After reading the documentation, I'm still not quite clear on how to use a QEventLoop to do idle processing in a QThread. Basically I want to have a thread that connects a device. Once a device is connected I want to use idle processing to poll the device. The user can terminate the thread using the quit function (which disconnects the device if necessary). As far as I understand, this is how you make your thread enter an event loop when it is started.


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

So my question basically is: where do I put the code that does the polling of the device?


To make your application perform idle processing (i.e. executing a
special function whenever there are no pending events), use a
QTimer with 0 timeout. More sophisticated idle processing schemes
can be achieved using processEvents().

Can someone post an example for this? What are the " sophisticated idle processing schemes"?

Here is a rough overview of how my run function would look without an event loop:


void MyThread::run()
{
connect_device();

done = false;

while(!done)
{
poll_device();
}

disconnect_device();
}

void MyThread::quit()
{
done = true;
}

jacek
25th February 2008, 12:18
Can someone post an example for this?
You will need an object that will do the polling:

class Poller : public QObject
{
Q_OBJECT
public:
Poller();
~Poller();

public slots:
void poll();
void stop();

private:
QTimer timer;
};

Poller::Poller()
{
// open the device
connect( &timer, SIGNAL( timeout() ), this, SLOT( poll() ) );
timer.start( 100 ); // wait at least 100 ms between polling
}


Now all you have to do is to create such Poller object in your thread. Note that if this is the only object that lives in that thread, it will be always "idle".


What are the " sophisticated idle processing schemes"?
You can pass some flags to processEvents() to control what events should be processed.

kloffy
25th February 2008, 22:51
Thank you! That was exactly what I was looking for. It just so happens that I stumbled upon a similar example right before I saw your post. It had suspend function to stop the timer, which is pretty neat. If anyone's interested:


class MyThreadObject : public QObject {
QTimer myTimer;

MyThreadObject() {
connect( &myTimer, timeout(), this, onTimeOut());
}

slot onSuspendThread() {
myTimer.stop();
}

slot onResumeThread() {
myTimer.start(0);
}

private slot onTimeOut() {
// do a few comutation steps.
// Should return "quickly" to the event loop
}

}


class MyThread : public QThread {
slot MyThread:run() {
MyThreadObject threadObject; //=> the threadObject affinity is the thread
// here, connect the appropriate signals to the appropriate slots

threadObject.onResumeThread();
exec();
}

}

wysota
26th February 2008, 01:01
I would just like to notice that if this is the only object living in your thread, there is no point in having the event loop in the thread. You can as well use this:


void Thread::run() {
while(!finished){
poll();
QThread::msleep(100);
}
}

kloffy
27th February 2008, 16:45
I would just like to notice that if this is the only object living in your thread, there is no point in having the event loop in the thread.
Yes, but if I understood correctly then it wouldn't be possible to connect signals/slots with objects in other threads. (Using queued connections)

wysota
27th February 2008, 19:20
It would be possible. You could emit signals but you couldn't have queued slots. If you have a single object that only polls for data, you don't need slots, so...

Mhondoz
6th March 2008, 13:12
I would just like to notice that if this is the only object living in your thread, there is no point in having the event loop in the thread. You can as well use this:


void Thread::run() {
while(!finished){
poll();
QThread::msleep(100);
}
}

This was my first approach to a similar problem. But, what would be the best way to stop the thread then?

Since I want to terminate the thread from "outside", I guess that would be to set the finished variable to true and then call wait(). But then again the finished variable will be accessed from two different threads and need to be protected by a Mutex?

wysota
6th March 2008, 14:19
This was my first approach to a similar problem. But, what would be the best way to stop the thread then?
Set the "finished" flag to true.


But then again the finished variable will be accessed from two different threads and need to be protected by a Mutex?

No, it doesn't, because only one thread may change the flag. The worst that may happen is that the thread will perform once cycle more, which is completely ignorable.

Mhondoz
6th March 2008, 14:48
Ok, thanks. :-)

So if I understand correctly, this will work and is thread safe:

mythread.h


class MyThread : public QThread {

public:
MyThread(QObject *parent);
~MyThread();

protected:
void run();

private:
bool _bFinished;

};


mythread.cpp


MyThread::MyThread(QObject *parent) :
QThread(parent), _bFinished( false ) {

start();
}

MyThread::~MyThread() {

_bFinished = true;
wait();

}

void MyThread::run() {

while ( ! _bFinished ) {

// Do something

QThread::msleep( 100 );
}
}


What I want to accomplish here is to have the thread start when it is constructed, and stop the thread in the destructor. My concern was the _bFinished variable which I believe is accessed by the MyThread thread in the while loop in run(), but in the destructor it will be accessed by the thread that constructed MyThread, for example the main thread...

wysota
6th March 2008, 15:33
It doesn't matter as nothing tries to set _bFinished to false when it's true. And that's the only case when a newly set value could be overwritten with a different value. If you are not sure about it, use QAtomicInt (available since 4.4, so the link is invalid at the time of writing this post).

Mhondoz
6th March 2008, 16:52
That makes sense. Thank you very much. :-)

jlemaitre
28th April 2011, 09:58
I all,


I would just like to notice that if this is the only object living in your thread, there is no point in having the event loop in the thread. You can as well use this:


void Thread::run() {
while(!finished){
poll();
QThread::msleep(100);
}
}

In that case, the polling will happen every 100ms, not doing idle processing. To perform idle processing, a timer with 0 timeout and an event loop are mandatory. Am I correct?

Thanks

wysota
28th April 2011, 10:02
In that case, the polling will happen every 100ms and the event loop is mandatory to perform idle processing. Am I correct?

Sleep and event loops don't go together well. If you want to do idle processing in the thread besides the main task the thread is doing, use a timer instead of sleep().

jlemaitre
28th April 2011, 11:13
If you want to do idle processing in the thread besides the main task the thread is doing, use a timer instead of sleep().

In the initial post of the op, I understood the idle processing is the main task. (Polling the device is the only task of the thread, and it must be done at idle time.) In that case, is its timer-based solution (re-coded below) appropriate?



class DevicePoller : public QThread
{
Q_OBJECT
QTimer m_timer;
slots:
void poll();
protected:
virtual void run()
{
connect( m_timer, SIGNAL( timeout() ), this, SLOT( poll() ) );
m_timer.start( 0 );
exec();
}
};


Or, is it best to avoid timers at all, and launch a thread with QThread::IdlePriority:



class DevicePoller : public QThread
{
protected:
virtual void run()
{
while( !finished ) poll();
}
};
DevicePoller mythread;
mythread.start( QThread::IdlePriority );
// ...


Or, am I wrong and there is another strategy?

Thanks for the rapid answer anyway!

wysota
28th April 2011, 11:41
In the initial post of the op, I understood the idle processing is the main task. (Polling the device is the only task of the thread, and it must be done at idle time.) In that case, is its timer-based solution (re-coded below) appropriate?
It's a periodic task, not idle processing.




class DevicePoller : public QThread
{
Q_OBJECT
QTimer m_timer;
slots:
void poll();
protected:
virtual void run()
{
connect( m_timer, SIGNAL( timeout() ), this, SLOT( poll() ) );
m_timer.start( 0 );
exec();
}
};

There is no point in using threads here, you can use the main thread with this approach.


Or, is it best to avoid timers at all, and launch a thread with QThread::IdlePriority:



class DevicePoller : public QThread
{
protected:
virtual void run()
{
while( !finished ) poll();
}
};
DevicePoller mythread;
mythread.start( QThread::IdlePriority );
// ...


Or, am I wrong and there is another strategy?
Thread priorities are just hints for the OS, it may ignore them so that's not a good approach -- you'll end up with a busy loop.