Mastering C++ Multithreading
上QQ阅读APP看书,第一时间看更新

QThread

A QThread class in Qt is not a thread, but an extensive wrapper around a thread instance, which adds signal-slot communication, runtime support, and other features. This is reflected in the basic usage of a QThread, as shown in the following code:

class Worker : public QObject { 
Q_OBJECT

public:
Worker();
~Worker();

public slots:
void process();

signals:
void finished();
void error(QString err);

private:
};

This preceding code is a basic Worker class, which will contain our business logic. It derives from the QObject class, which also allows us to use signal-slot and other intrinsic QObject features. Signal-slot architecture at its core is simply a way for listeners to register on (connect to) signals declared by QObject-derived classes, allowing for cross-module, cross-thread and asynchronous communication.

It has a single, which can be called to start processing, and two signals--one to signal completion, and one to signal an error.

The implementation would look like the following:

Worker::Worker() { }  
Worker::~Worker() { }

void Worker::process() {
qDebug("Hello World!");
emit finished();
}

The constructor could be extended to include parameters. Any heap-allocated variables (using malloc or new) must be allocated in the process() method, and not in the constructor due to the thread context the Worker instance will be operating in, as we will see in a moment.

To create a new QThread, we would use the following setup:

QThread* thread = new QThread; 
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

The basic procedure is to create a new QThread instance on the heap (so it won't go out of scope) along with a heap-allocated instance of our Worker class. This new worker would then be moved to the new thread instance using its moveToThread() method.

Next, one will connect the various signals to relevant slots including our own finished() and error() signals. The started() signal from the thread instance would be connected to the slot on our worker which will start it.

Most importantly, one has to connect some kind of completion signal from the worker to the quit() and deleteLater() slots on the thread. The finished() signal from the thread will then be connected to the deleteLater() slot on the worker. This will ensure that both the thread and worker instances are cleaned up when the worker is done.