The Qt documentation for QThread says to create a class from QThread, and to implement the run method.
Below is taken from the 4.7 Qthread documentation...
To create your own threads, subclass QThread and reimplement run(). For example:
class MyThread : public QThread { public: void run(); }; void MyThread::run() { QTcpSocket socket; // connect QTcpSocket's signals somewhere meaningful ... socket.connectToHost(hostName, portNumber); exec(); }
So in every single thread I've created, I've done just that and for most things it works just fine (I do not implement moveToThread(this) in any of my objects and it works great).
I hit a snag last week (managed to get through it by working around where I created my objects) and found the following blog post. Here is basically says that subclassing QThread really isn't the correct way to do it (and that the documentation is incorrect).
This is coming from a Qt developer so at first glance I was interested and upon further reflection, agree with him. Following OO principles, you really only want to subclass a class to further enhance that class... not to just use the classes methods directly... thats why you instantiate...
Lets say I wanted to move a custom QObject class to a thread... what would be the 'correct' way of doing it? In that blog post, he 'says' he has an example somewhere... but if someone could further explain it to me it'd be greatly appreciated!
Update:
Since this question gets so much attention, here is a copy and paste of the 4.8 documentation with the 'proper' way to implement a QThread.
class Worker : public QObject { Q_OBJECT QThread workerThread; public slots: void doWork(const QString ¶meter) { // ... emit resultReady(result); } signals: void resultReady(const QString &result); }; class Controller : public QObject { Q_OBJECT QThread workerThread; public: Controller() { Worker *worker = new Worker; worker->moveToThread(&workerThread); connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString))); connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString))); workerThread.start(); } ~Controller() { workerThread.quit(); workerThread.wait(); } public slots: void handleResults(const QString &); signals: void operate(const QString &); };
I still believe that it is worthwhile to point out that they include an extra Worker::workerThread
member that is unnecessary and is never used in their example. Remove that piece and it is a proper example of how to do threading in Qt.
To use it, prepare a QObject subclass with all your desired functionality in it. Then create a new QThread instance, push the QObject onto it using moveToThread(QThread*) of the QObject instance and call start() on the QThread instance. That's all.
QThread will notify you via a signal when the thread is started() and finished(), or you can use isFinished() and isRunning() to query the state of the thread. You can stop the thread by calling exit() or quit(). In extreme cases, you may want to forcibly terminate() an executing thread.
About the only thing I can think of to add is to further state that QObject
s have an affinity with a single thread. This is usually the thread that creates the QObject
. So if you create a QObject
in the app's main thread and want to use it in another thread, you need to use moveToThread()
to change the affinity.
This saves having to subclass QThread
and creating your objects in the run()
method, thus keeping your stuff nicely encapsulated.
That blog post does include a link to an example. It is pretty short but it shows the basic idea. Create your QObject
s, connect your signals, create your QThread
, move your QObjects
to the QThread
and start the thread. The signal/slot mechanisms will ensure that thread boundaries are crossed properly and safely.
You may have to introduce synchronization if you have to call methods on your object outside of that mechanism.
I know Qt has some other nice threading facilities beyond threads that are probably worth getting familiar with but I have yet to do so :)
Here's one example of how to use QThread correctly, but it has some issues with it, which are reflected in the comments. In particular, since the order in which the slots are executed isn't strictly defined, it could lead to various problems. The comment posted on August 6, 2013 gives a nice idea how to deal with this issue. I use something like that in my program, and here's some example code to clarify.
The basic idea is the same: I create a QThread instance that lives in my main thread, a worker class instance that lives in the new thread I created, and then I connect all the signals.
void ChildProcesses::start() { QThread *childrenWatcherThread = new QThread(); ChildrenWatcher *childrenWatcher = new ChildrenWatcher(); childrenWatcher->moveToThread(childrenWatcherThread); // These three signals carry the "outcome" of the worker job. connect(childrenWatcher, SIGNAL(exited(int, int)), SLOT(onChildExited(int, int))); connect(childrenWatcher, SIGNAL(signalled(int, int)), SLOT(onChildSignalled(int, int))); connect(childrenWatcher, SIGNAL(stateChanged(int)), SLOT(onChildStateChanged(int))); // Make the watcher watch when the thread starts: connect(childrenWatcherThread, SIGNAL(started()), childrenWatcher, SLOT(watch())); // Make the watcher set its 'stop' flag when we're done. // This is performed while the watch() method is still running, // so we need to execute it concurrently from this thread, // hence the Qt::DirectConnection. The stop() method is thread-safe // (uses a mutex to set the flag). connect(this, SIGNAL(stopped()), childrenWatcher, SLOT(stop()), Qt::DirectConnection); // Make the thread quit when the watcher self-destructs: connect(childrenWatcher, SIGNAL(destroyed()), childrenWatcherThread, SLOT(quit())); // Make the thread self-destruct when it finishes, // or rather, make the main thread delete it: connect(childrenWatcherThread, SIGNAL(finished()), childrenWatcherThread, SLOT(deleteLater())); childrenWatcherThread->start(); }
Some background:
The ChildProcesses class is a child process manager that starts new child processes with spawn() calls, keeps the list of the processes currently running and so on. However, it needs to keep track of the children states, which means using waitpid() call on Linux or WaitForMultipleObjects on Windows. I used to call these in non-blocking mode using a timer, but now I want more prompt reaction, which means blocking mode. That's where the thread comes in.
The ChildrenWatcher class is defined as follows:
class ChildrenWatcher: public QObject { Q_OBJECT private: QMutex mutex; bool stopped; bool isStopped(); public: ChildrenWatcher(); public slots: /// This is the method which runs in the thread. void watch(); /// Sets the stop flag. void stop(); signals: /// A child process exited normally. void exited(int ospid, int code); /// A child process crashed (Unix only). void signalled(int ospid, int signal); /// Something happened to a child (Unix only). void stateChanged(int ospid); };
Here how it works. When all this stuff is started, the ChildProcess::start() method is called (see above). It creates a new QThread and a new ChildrenWatcher, which is then moved to the new thread. Then I connect three signals which inform my manager about the fate of its child processes (exited/signalled/god-knows-what-happened). Then starts the main fun.
I connect QThread::started() to the ChildrenWatcher::watch() method so it is started as soon as the thread is ready. Since the watcher lives in the new thread, that's where the watch() method is executed (queued connection is used to call the slot).
Then I connect the ChildProcesses::stopped() signal to the ChildrenWatcher::stop() slot using Qt::DirectConnection because I need to do it asynchronously. This is needed so my thread stops when the ChildProcesses manager is no longer needed. The stop() method looks like this:
void ChildrenWatcher::stop() { mutex.lock(); stopped = true; mutex.unlock(); }
And then ChildrenWatcher::watch():
void ChildrenWatcher::watch() { while (!isStopped()) { // Blocking waitpid() call here. // Maybe emit one of the three informational signals here too. } // Self-destruct now! deleteLater(); }
Oh, and the isStopped() method is just a convenient way to use a mutex in the while() condition:
bool ChildrenWatcher::isStopped() { bool stopped; mutex.lock(); stopped = this->stopped; mutex.unlock(); return stopped; }
So what happens here is that I set the stopped flag when I need to finish, and then the next time isStopped() is called it returns false and the thread ends.
So what happens when the watch() loop ends? It calls deleteLater() so the object self-destructs as soon as control is returned to the thread event loop which happens just right after the deleteLater() call (when watch() returns). Going back to ChildProcesses::start(), you can see that there is a connection from the destroyed() signal of the watcher to the quit() slot of the thread. This means that the thread automatically finishes when the watcher is done. And when it's finished, it self-destructs too because its own finished() signal is connected to its deleteLater() slot.
This is pretty much the same idea as Maya posted, but because I use the self-destruct idiom, I don't need to depend on the sequence in which the slots are called. It's always self-destruct first, stop thread later, then it self-destructs too. I could define a finished() signal in the worker, and then connect it to its own deleteLater(), but that would only mean one connection more. Since I don't need a finished() signal for any other purpose, I chose to just call deleteLater() from the worker itself.
Maya also mentions that you shouldn't allocate new QObjects in the worker's constructor because they won't live in the thread you move the worker to. I'd say do it anyway because that's the way OOP works. Just make sure all those QObjects are children of the worker (that is, use the QObject(QObject*) constructor) - moveToThread() moves all the children along with the object being moved. If you really need to have QObjects that aren't children of your object, then override moveToThread() in your worker so it moves all the necessary stuff too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With