From 0b83e9e665ec410610289242a821d55e445c8506 Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Tue, 19 Feb 2013 20:19:25 +0800 Subject: 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 207f588b6896cbe72745037dc1cb0a3aef1cf6d0. 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. This is a modified backport of qtbase commit 91e12dca757a8ef5c4691b70eb80db61a9d47e83 Change-Id: I4932aef00307a6cf91d57d632a02b8a85e5e8845 Reviewed-by: Olivier Goffart --- .../snippets/code/src_corelib_thread_qthread.cpp | 80 +++++++++++++++------- src/corelib/kernel/qobject.cpp | 6 +- src/corelib/thread/qthread.cpp | 62 +++++++---------- 3 files changed, 83 insertions(+), 65 deletions(-) diff --git a/doc/src/snippets/code/src_corelib_thread_qthread.cpp b/doc/src/snippets/code/src_corelib_thread_qthread.cpp index 35f1e34..408f90f 100644 --- a/doc/src/snippets/code/src_corelib_thread_qthread.cpp +++ b/doc/src/snippets/code/src_corelib_thread_qthread.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Olivier Goffart ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. @@ -38,41 +38,69 @@ ** ****************************************************************************/ -//! [0] -class Worker : public QObject +#include +class MyObject; + +//! [reimpl-run] +class WorkerThread : public QThread { Q_OBJECT - -public slots: - void doWork() { - ... + void run() { + QString result; + /* expensive or blocking operation */ + emit resultReady(result); } +signals: + void resultReady(const QString &s); }; -void MyObject::putWorkerInAThread() +void MyObject::startWorkInAThread() { - Worker *worker = new Worker; - QThread *workerThread = new QThread(this); - - connect(workerThread, SIGNAL(started()), worker, SLOT(doWork())); - connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater())); - worker->moveToThread(workerThread); - - // Starts an event loop, and emits workerThread->started() + WorkerThread *workerThread = new WorkerThread(this); + connect(workerThread, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString))); + connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); workerThread->start(); } -//! [0] +//! [reimpl-run] + -//! [1] -class AdvancedThreadManager : public QThread +//! [worker] +class Worker : public QObject { -protected: - void run() - { - /* ... other code to initialize thread... */ + Q_OBJECT + QThread workerThread; - // Begin event handling - exec(); +public slots: + void doWork(const QString ¶meter) { + // ... + emit resultReady(result); } + +signals: + void resultReady(const QString &result); }; -//! [1] + +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 &); +}; +//! [worker] + diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 2ff498a..b355e33 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2065,7 +2065,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 diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 07de812..53e4d5e 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -203,21 +203,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 doc/src/snippets/code/src_corelib_thread_qthread.cpp 0 + You can use worker objects by moving them to the thread using + QObject::moveToThread(). + + \snippet doc/src/snippets/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. @@ -225,7 +242,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 @@ -260,28 +276,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 doc/src/snippets/code/src_corelib_thread_qthread.cpp 1 - QThread also provides static, platform independent sleep functions: sleep(), msleep(), and usleep() allow full second, millisecond, and microsecond resolution respectively. @@ -291,14 +285,6 @@ QThreadPrivate::~QThreadPrivate() wait(), consider listening for the finished() signal. Instead of the sleep() functions, consider using QTimer. - 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} */ -- cgit v0.12