PDA

View Full Version : Correct Threading Technique



themusicalguy
29th October 2007, 14:32
Hi,

I'm wondering if anyone can advise me on the best way to do the following:

I have an object Runner which is periodically passed a list of Commands to run. The Runner should execute the Commands in a new thread so as not to block the GUI. Once a Command list has been run the thread should sleep until a new Command list is assigned. However, I'm having a little trouble working out how to do this.

At the moment I'm thinking of having Runner::run() execute a semi-busy loop which periodically checks whether a 'newList' flag has been set. When Runner::execute(CommandList * cList) is called from the main thread the Command list will be assigned to the Runner and the newList flag set to true causing the Runner::run() thread to execute the Command list. This would look something like the following:



void Runner::run() {
forever {
if(newList) {
newList = false;
// Execute test.
emit runComplete();
} else {
sleep(1);
}
}
}

void Runner::execute(CommandList * cList) {
currentTest = cList;
newTest = true;
}


Is this a typical solution or is there a nicer more Qt (and possibly more CPU friendly) way of doing this?

ChristianEhrlicher
29th October 2007, 14:41
I rather would use a QSemaphore to avoid busy waiting. Or enter the QThread eventloop and use signals/slots.

jacek
29th October 2007, 14:43
The classical approach is to use a wait condition (http://doc.trolltech.com/latest/qwaitcondition) (or similar mechanism) to block the thread until there's a task ready, but as Christian has already said, you can also pass the tasks through queued connections or you can try out QtConcurrent (http://labs.trolltech.com/page/Projects/Threads/QtConcurrent).

themusicalguy
29th October 2007, 14:56
The classical approach is to use a wait condition (http://doc.trolltech.com/latest/qwaitcondition) (or similar mechanism) to block the thread until there's a task ready, but as Christian has already said, you can also pass the tasks through queued connections or you can try out QtConcurrent (http://labs.trolltech.com/page/Projects/Threads/QtConcurrent).

I've had a look over the QWaitCondition documentation and I must confess I'm still not sure how to use it, at least not how to apply it to my situation. Could you give a small example if it isn't too much trouble?

In regards to using the QThreads event loop and the signals / slot approach, as I understand it Runner would still exist in the main thread, only the Runner::run() method would be in the new thread therefore any signal / slot connections would be processed in the main thread anyway.

marcel
29th October 2007, 15:03
In regards to using the QThreads event loop and the signals / slot approach, as I understand it Runner would still exist in the main thread, only the Runner::run() method would be in the new thread therefore any signal / slot connections would be processed in the main thread anyway.

Not if you move the thread object to the worker thread, with moveToThread.

themusicalguy
29th October 2007, 16:16
Not if you move the thread object to the worker thread, with moveToThread.

Hmmm, I didn't realise you could move the actual QThread implementation itself to its own thread. This might be the way to go.

Thanks

jacek
29th October 2007, 16:34
I've had a look over the QWaitCondition documentation and I must confess I'm still not sure how to use it, at least not how to apply it to my situation. Could you give a small example if it isn't too much trouble?


// thread's part:
forever {
mutex.lock();
while( queue.empty() ) {
queueNotEmptyCondition.wait( & mutex );
}
Task * task = getTaskFromQueue();
mutex.unlock();

task->process();
delete task;
};

// task scheduling:
mutex.lock();
queue.append( task );
mutex.unlock();
queueNotEmptyCondition.wakeOne();

Of course if you use queued connections, Qt does most of this for you.


as I understand it Runner would still exist in the main thread, only the Runner::run() method would be in the new thread therefore any signal / slot connections would be processed in the main thread anyway.
Yes, it's a bit problematic in Qt, but you can use moveToThread() as Marcel has suggested or you can split implementation into Runner object and a RunnerThread that hosts it.

themusicalguy
31st October 2007, 15:53
So, just to clarify...

It is possible to move a QThread subclass to it's own thread (i.e. the one that it created) using moveToThread() once it's QThread::run() has been invoked?

I can't wait until QThread is made non-abstract, it will save so much missunderstanding about thread affinity.

jacek
31st October 2007, 18:38
It is possible to move a QThread subclass to it's own thread (i.e. the one that it created) using moveToThread() once it's QThread::run() has been invoked?
Yes, but there might be problem after the thread stops, since it will belong to a non-existent thread. Other solution is to place some object inside the worker thread that will receive queued signals and forward them to QThread subclass through a direct connection.


I can't wait until QThread is made non-abstract, it will save so much missunderstanding about thread affinity.
You don't have to wait --- just do it yourself.

class Thread : public QThread
{
Q_OBJECT
public:
Thread( QObject * parent = 0 ) : QThread( parent ) {}
protected:
void run() { exec(); }
};
...
Thread *t = new Thread();
t->start();
Worker *w = new Worker();
w->moveToThread( t );
...

themusicalguy
2nd November 2007, 11:27
Great stuff thanks a lot, I think this will do the trick.

One other question... If I use the above method, any objects that are created by Worker will have affinity to the created thread. Now if a pointer to an object created by Worker (say object A) was passed to an object in the main thread and then the created thread was to end, would that cause any problems with object A?

My situation is as follows:

Object structure in main thread creates Test objects.
Runner in thread A runs Test objects and generates TestRun objects.
TestRun objects are passed out to Object structure in main thread.

I'm thinking that after each TestRun object is created the Runner thread (thread A) should then move it to the main thread before passing it out. Would this be the best solution?

jacek
2nd November 2007, 15:28
if I use the above method, any objects that are created by Worker will have affinity to the created thread. Now if a pointer to an object created by Worker (say object A) was passed to an object in the main thread and then the created thread was to end, would that cause any problems with object A?
Thread affinity is important only for QObjects, but you have to watch out for object ownership.


I'm thinking that after each TestRun object is created the Runner thread (thread A) should then move it to the main thread before passing it out.
There might be a problem only if TestRun is a QObject.