QThread documentation: do not discourage the reimplementation of QThread

The new QThread documentation now really discourage to reimplement
QThread. But in fact, there are many cases where it is perfectly fine.
And the example given is even a case where using worker object is wrong.
The examle even contains a leak since the thread will never stop and
will even leak.

This changes put back some sentences from before commit
d4ad9dbbf9.

The sample code has been re-writen. Notice how reimpementing run takes
less lines of code, less runtime overhead, no leaks, and also is more
complete than the previous example.

Change-Id: I6cb80826e917dd5ce442ccad2572ec692ccb25ab
Reviewed-by: Andre Somers <andre@familiesomers.nl>
Reviewed-by: Geir Vattekar <geir.vattekar@digia.com>
Reviewed-by: Debao Zhang <hello@debao.me>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Olivier Goffart 2013-01-19 16:17:51 +01:00 committed by The Qt Project
parent 86ca28774b
commit 91e12dca75
3 changed files with 84 additions and 67 deletions

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the documentation of the Qt Toolkit.
@ -38,41 +38,68 @@
**
****************************************************************************/
//! [0]
#include <QtCore/QThread>
class MyObject;
//! [reimpl-run]
class WorkerThread : public QThread
{
Q_OBJECT
void run() Q_DECL_OVERRIDE {
QString result;
/* expensive or blocking operation */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
//! [reimpl-run]
//! [worker]
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
public slots:
void doWork() {
...
void doWork(const QString &parameter) {
// ...
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
void MyObject::putWorkerInAThread()
class Controller : public QObject
{
Worker *worker = new Worker;
QThread *workerThread = new QThread(this);
connect(workerThread, &QThread::started, worker, &Worker::doWork);
connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);
worker->moveToThread(workerThread);
// Starts an event loop, and emits workerThread->started()
workerThread->start();
}
//! [0]
//! [1]
class AdvancedThreadManager : public QThread
{
protected:
void run()
{
/* ... other code to initialize thread... */
// Begin event handling
exec();
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
//! [1]
//! [worker]

View File

@ -1917,7 +1917,11 @@ void QObject::removeEventFilter(QObject *obj)
loop. If the event loop is not running when this function is
called (e.g. deleteLater() is called on an object before
QCoreApplication::exec()), the object will be deleted once the
event loop is started.
event loop is started. If deleteLater() is called after the main event loop
has stopped, the object will not be deleted.
Since Qt 4.8, if deleteLater() is called on an object that lives in a
thread with no running event loop, the object will be destroyed when the
thread finishes.
Note that entering and leaving a new event loop (e.g., by opening a modal
dialog) will \e not perform the deferred deletion; for the object to be

View File

@ -178,21 +178,38 @@ QThreadPrivate::~QThreadPrivate()
\ingroup thread
A QThread object manages one thread of control within the
program. To make code run in a separate thread, simply create a
QThread, change the thread affinity of the QObject(s) that
contain the code, and start() the new event loop. For example:
program. QThreads begin executing in run(). By default, run() starts the
event loop by calling exec() and runs a Qt event loop inside the thread.
\snippet code/src_corelib_thread_qthread.cpp 0
You can use worker objects by moving them to the thread using
QObject::moveToThread.
\snippet code/src_corelib_thread_qthread.cpp worker
The code inside the Worker's slot would then execute in a
separate thread. In this example, the QThread triggers the
Worker's doWork() slot upon starting, and frees the Worker's
memory upon terminating. However, you are free to connect the
separate thread. However, you are free to connect the
Worker's slots to any signal, from any object, in any thread. It
is safe to connect signals and slots across different threads,
thanks to a mechanism called \l{Qt::QueuedConnection}{queued
connections}.
Another way to make code run in a separate thread, is to subclass QThread
and reimplement run(). For example:
\snippet code/src_corelib_thread_qthread.cpp reimpl-run
In that example, the thread will exit after the run function has returned.
There will not be any event loop running in the thread unless you call
exec().
It is important to remember that a QThread object usually lives
in the thread where it was created, not in the thread that it
manages. This oft-overlooked detail means that a QThread's slots
will be executed in the context of its home thread, not in the
context of the thread it is managing. For this reason,
implementing new slots in a QThread subclass is error-prone and
discouraged.
\note If you interact with an object, using any technique other
than queued signal/slot connections (e.g. direct function calls),
then the usual multithreading precautions need to be taken.
@ -200,7 +217,6 @@ QThreadPrivate::~QThreadPrivate()
\note It is not possible to change the thread affinity of GUI
objects; they must remain in the main thread.
\section1 Managing threads
QThread will notifiy you via a signal when the thread is
@ -244,36 +260,6 @@ QThreadPrivate::~QThreadPrivate()
\l{Mandelbrot Example}, as that is the name of the QThread subclass).
Note that this is currently not available with release builds on Windows.
\section1 Subclassing QThread
Subclassing QThread is unnecessary for most purposes, since
QThread provides fully-functional thread management capabilities.
Nonetheless, QThread can be subclassed if you wish to implement
advanced thread management. This is done by adding new member
functions to the subclass, and/or by reimplementing run().
QThread's run() function is analogous to an application's main()
function -- it is executed when the thread is started, and the
thread will end when it returns.
\note Prior to Qt 4.4, the only way to use QThread for parallel
processing was to subclass it and implement the processing code
inside run(). This approach is now considered \b {bad practice};
a QThread should only manage a thread, not process data.
If you require event handling and signal/slot connections to
work in your thread, and if you reimplement run(), you must
explicitly call exec() at the end of your reimplementation:
\snippet code/src_corelib_thread_qthread.cpp 1
It is important to remember that a QThread object usually lives
in the thread where it was created, not in the thread that it
manages. This oft-overlooked detail means that a QThread's slots
will be executed in the context of its home thread, not in the
context of the thread it is managing. For this reason,
implementing new slots in a QThread subclass is error-prone and
discouraged.
\sa {Thread Support in Qt}, QThreadStorage, QMutex, QSemaphore, QWaitCondition,
{Mandelbrot Example}, {Semaphores Example}, {Wait Conditions Example}
*/