Synchronized Threads (Part 1)
Suppose you want to do some work in another thread. Qt gives you the right tools — QThread, QMutex, QWaitCondition, and even signals and slots across threads — but there is still some tedious and error-prone work left even for basic situations.
First of all, there’s no “simple” way to know when the services of a remote thread can be referenced. QThread has a started() signal, but I find it to be nearly useless. Just because some thread has started does not mean you can start calling methods on objects in that thread. How do you know the object pointers? Have the objects even been made yet? Is there any initialization that needs to be done in the remote objects before you can start shooting signals all over the place? What we really want is a signal that is emitted not only after the thread starts, but after some of our own initialization occurs within that thread.
That said, I also consider an asynchronous initialization notification to be overkill in the general case. Starting a thread should take no time at all, nor should the initialization of some objects. Synchronizing the startup of the thread greatly decreases the complexity of the program. Similarly, I think it is worthwhile to synchronize shutdown.
Well, here we go:
class SyncThread : public QThread
{
Q_OBJECT
public:
QMutex m;
QWaitCondition w;
QEventLoop *loop;
SyncThread(QObject *parent = 0) : QThread(parent)
{
loop = 0;
}
~SyncThread()
{
stop();
}
void start()
{
QMutexLocker locker(&m);
QThread::start();
w.wait(&m);
}
void stop()
{
QMutexLocker locker(&m);
if(!loop)
return;
QMetaObject::invokeMethod(loop, "quit");
w.wait(&m);
wait();
}
protected:
virtual void run()
{
m.lock();
loop = new QEventLoop;
atStart();
w.wakeOne();
m.unlock();
loop->exec ();
m.lock();
atEnd();
delete loop;
loop = 0;
w.wakeOne();
m.unlock();
}
virtual void atStart() = 0;
virtual void atEnd() = 0;
};
So, what does this all mean? Well, now, instead of inheriting QThread and implementing the run() function, you can inherit SyncThread and implement the atStart() and atEnd() functions.
Suppose you make a class called MyThread that inherits SyncThread:
class MyThread : public SyncThread
{
Q_OBJECT
public:
SomeObject *obj;
MyThread(QObject *parent) : SyncThread(parent)
{
obj = 0;
}
protected:
void atStart()
{
obj = new SomeObject;
}
void atEnd()
{
delete obj;
}
};
Now, you can call start() to begin the thread, and SomeObject is guaranteed to be accessible when start() returns.
MyThread *thread = new MyThread;
thread->start();
thread->obj->foo();
delete thread;
Stopping the thread is also cleanly synchronous. The eventloop of the other thread is stopped, atEnd is called, and the thread itself stops, all before the delete finishes.
If you keep your atStart/atEnd reasonable, that is, you don’t put more code in them than you’d normally put into one pass of the event loop, the performance trade-off should be negligable.
Look above at MyThread again just to see how simple it is. There isn’t even a mutex! Stick to signals and slots when interacting with SomeObject, and you can have safe multithreaded code without even trying.


