PDA

View Full Version : Qt Threading



avh
30th May 2008, 18:12
So, I'm a situation that's similar to the Mandelbrot example that ships with Qt, with a few wrinkles. I'm writing a GUI for a scientific computing application, and it needs to stay responsive while the application is doing something computationally expensive.

Unlike the Mandelbrot example, where the computationally expensive code appears directly in the worker thread, it has already been implemented and lives in a global object, whose member functions I call in order to interface with that code. I have a QThread subclass, appearing below, that I want to use as a worker thread, and it has some slots that call the global object's troublesome member functions.

Everything compiles and runs, but, when something happens that triggers one of the QThread's slots, the GUI still goes nonresponsive.

I think this might be because the GUI also relies on member functions of that global object for various things, or maybe I just have an oversimplified conception of how to use a QThread.

Regardless, my question is this: how does using a QThread to keep a GUI responsive change when one is calling member functions of a global object like I am attempting to do?



class ReconstructionThread : public QThread {
Q_OBJECT

public:
ReconstructionThread(QObject* parent) : QThread(parent) {}
void run();

public slots:
void startMethod(int iToStart);
void stopMethod(int iToStop);

signals:
void methodFinished();
};

#include "ReconstructionThread.h"

extern ImagingDetectionInterconnect interconnect;

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

void ReconstructionThread::startMethod(int iToStart) {
interconnect.start(iToStart);//This is the troublemaker, here: it can take an hour
//or two to return
emit methodFinished();
}

void ReconstructionThread::stopMethod(int iToStop) {
interconnect.stop(iToStop);
}

jpn
30th May 2008, 18:35
Slots in QThread subclass are troublemakers. QThread object itself lives in the creating thread, not in the thread executed in run(). Please read Multithreading by Brad Hughes (http://chaos.troll.no/~ahanssen/devdays2007/DevDays2007-Threading.pdf), starting from page 33.

avh
30th May 2008, 19:12
Does that mean that member functions of QThread subclasses are also executed in the creating thread?

jpn
30th May 2008, 19:27
Exactly. But you can easily work it around by creating another object which lives in the correct thread and move those slots there:


class ReconstructionObject : public QObject {
...
public slots:
void startMethod(int iToStart);
void stopMethod(int iToStop);

signals:
void methodFinished();
};

Then you have two options: 1) create ReconstructionObject in ReconstructionThread::run():


void ReconstructionThread::run()
{
ReconstructionObject object;
// connect signals and slots
exec();
}

or 2) create it somewhere outside and use QObject::moveToThread():


ReconstructionThread* thread = new ReconstructionThread(parent);
ReconstructionObject* object = new ReconstructionObject;
object->moveToThread(thread);
thread->start();

// and for example something like this to handle cleanup
connect(object, SIGNAL(methodFinished()), object, SLOT(deleteLater()));
connect(object, SIGNAL(destroyed()), thread , SLOT(quit()));
connect(thread, SIGNAL(finished()), thread , SLOT(deleteLater()));

avh
30th May 2008, 19:49
So, I understand now more or less how to get interconnect.start(int) executing in the right thread. However, I have one more concern.

interconnect is a global object, and some of its members will be called elsewhere in my application while waiting for interconnect.start(int) to return. Will that have bad results?

jpn
30th May 2008, 20:03
So, I understand now more or less how to get interconnect.start(int) executing in the right thread.
Great! :)

Btw, you can use something like:

qDebug() << "ReconstructionThread::startMethod():" << QThread::currentThread() << "vs. main thread:" << qApp->thread();
to output useful information about the current thread.



However, I have one more concern.

interconnect is a global object, and some of its members will be called elsewhere in my application while waiting for interconnect.start(int) to return. Will that have bad results?
Usually it's not a very good idea. You should at least protect data with mutexes to avoid concurrent read and write of same data. But I would rather consider not using a global object at all. ;)

avh
30th May 2008, 20:16
The global object wasn't my decision; I'm just writing the GUI.

jpn
30th May 2008, 20:20
Ok, so then the solution is to make it thread safe (http://doc.trolltech.com/latest/threads.html#reentrancy-and-thread-safety) with help of mutexes or read-write locks.