From cba2d0443411b937586f259059e7f14c1cf5b512 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 20 Aug 2020 11:34:38 +0200 Subject: QtConcurrent: Add documentation for runWithPromise() Task-number: QTBUG-84702 Change-Id: Ic8233aeffbdbd1420bdbde7ad7d03f25cd438ea8 Reviewed-by: Paul Wicking --- .../code/src_concurrent_qtconcurrentrun.cpp | 86 +++++++++++ src/concurrent/doc/src/qtconcurrent-index.qdoc | 7 +- src/concurrent/qtconcurrentfilter.cpp | 2 +- src/concurrent/qtconcurrentmap.cpp | 2 +- src/concurrent/qtconcurrentrun.cpp | 159 +++++++++++++++++++-- src/concurrent/qtconcurrentrun.h | 6 + 6 files changed, 250 insertions(+), 12 deletions(-) diff --git a/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrentrun.cpp b/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrentrun.cpp index aba64ad94a..60d276cde6 100644 --- a/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrentrun.cpp +++ b/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrentrun.cpp @@ -144,3 +144,89 @@ QtConcurrent::run(TestClass(), 42).waitForFinished(); // Ill-formed QtConcurrent::run(&o, 42).waitForFinished(); // compilation error //! [8] + +//! [9] +extern void aFunction(QPromise &promise); +QFuture future = QtConcurrent::runWithPromise(aFunction); +//! [9] + +//! [10] +extern void aFunction(QPromise &promise, int arg1, const QString &arg2); + +int integer = ...; +QString string = ...; + +QFuture future = QtConcurrent::runWithPromise(aFunction, integer, string); +//! [10] + +//! [11] +void helloWorldFunction(QPromise &promise) +{ + promise.addResult("Hello"); + promise.addResult("world"); +} + +QFuture future = QtConcurrent::runWithPromise(helloWorldFunction); +... +QList results = future.results(); +//! [11] + +//! [12] +void aFunction(QPromise &promise) +{ + for (int i = 0; i < 100; ++i) { + promise.suspendIfRequested(); + if (promise.isCanceled()) + return; + + // computes the next result, may be time consuming like 1 second + const int res = ... ; + promise.addResult(res); + } +} + +QFuture future = QtConcurrent::runWithPromise(aFunction); + +... // user pressed a pause button after 10 seconds +future.suspend(); + +... // user pressed a resume button after 10 seconds +future.resume(); + +... // user pressed a cancel button after 10 seconds +future.cancel(); +//! [12] + +//! [13] +void aFunction(QPromise &promise) +{ + promise.setProgressRange(0, 100); + int result = 0; + for (int i = 0; i < 100; ++i) { + // computes some part of the task + const int part = ... ; + result += part; + promise.setProgressValue(i); + } + promise.addResult(result); +} + +QFutureWatcher watcher; +QObject::connect(&watcher, &QFutureWatcher::progressValueChanged, [](int progress){ + ... ; // update GUI with a progress + qDebug() << "current progress:" << progress; +}); +watcher.setFuture(QtConcurrent::runWithPromise(aFunction)); +//! [13] + +//! [14] +struct Functor { + void operator()(QPromise &) { } + void operator()(QPromise &) { } +}; + +Functor f; +runWithPromise(f); // this will select the 2nd overload +// runWithPromise(f); // error, both candidate overloads potentially match +//! [14] + diff --git a/src/concurrent/doc/src/qtconcurrent-index.qdoc b/src/concurrent/doc/src/qtconcurrent-index.qdoc index cf4711476c..acb516fb63 100644 --- a/src/concurrent/doc/src/qtconcurrent-index.qdoc +++ b/src/concurrent/doc/src/qtconcurrent-index.qdoc @@ -81,10 +81,15 @@ folded into a single result. \endlist - \li \l {Concurrent Run} + \li \l {Concurrent Run and Run With Promise} \list \li \l {QtConcurrent::run}{QtConcurrent::run()} runs a function in another thread. + \li \l {QtConcurrent::runWithPromise}{QtConcurrent::runWithPromise()} + is like run(), except that the function to run accepts additional + argument of QPromise type that enables more control over the function + execution, like suspending or canceling the execution when requested, + progress reporting or reporting multiple results. \endlist \li \l {Concurrent Task} diff --git a/src/concurrent/qtconcurrentfilter.cpp b/src/concurrent/qtconcurrentfilter.cpp index 223aad5b1f..9f4d962518 100644 --- a/src/concurrent/qtconcurrentfilter.cpp +++ b/src/concurrent/qtconcurrentfilter.cpp @@ -49,7 +49,7 @@ containing the filtered content, and QtConcurrent::filteredReduced() returns a single result. - These functions are a part of the \l {Qt Concurrent} framework. + These functions are part of the \l {Qt Concurrent} framework. Each of the above functions have a blocking variant that returns the final result instead of a QFuture. You use them in the same way as the diff --git a/src/concurrent/qtconcurrentmap.cpp b/src/concurrent/qtconcurrentmap.cpp index 26a4113303..9e5e6aab32 100644 --- a/src/concurrent/qtconcurrentmap.cpp +++ b/src/concurrent/qtconcurrentmap.cpp @@ -153,7 +153,7 @@ sequence containing the modified content, and QtConcurrent::mappedReduced() returns a single result. - These functions are a part of the \l {Qt Concurrent} framework. + These functions are part of the \l {Qt Concurrent} framework. Each of the above functions has a blocking variant that returns the final result instead of a QFuture. You use them in the same diff --git a/src/concurrent/qtconcurrentrun.cpp b/src/concurrent/qtconcurrentrun.cpp index c5f2e113a2..fecbb484ff 100644 --- a/src/concurrent/qtconcurrentrun.cpp +++ b/src/concurrent/qtconcurrentrun.cpp @@ -39,15 +39,27 @@ /*! \page qtconcurrentrun.html - \title Concurrent Run + \title Concurrent Run and Run With Promise \ingroup thread - The QtConcurrent::run() function runs a function in a separate thread. + The QtConcurrent::run() and QtConcurrent::runWithPromise() + functions run a function in a separate thread. The return value of the function is made available through the QFuture API. + The function passed to QtConcurrent::run() is able to report merely + a single computation result to its caller, while the function passed to + QtConcurrent::runWithPromise() can make use of the additional + QPromise API, which enables multiple result reporting, progress reporting, + suspending the computation when requested by the caller, or stopping + the computation on the caller's demand. - This function is a part of the \l {Qt Concurrent} framework. + These functions are part of the Qt Concurrent framework. - \section1 Running a Function in a Separate Thread + \section1 Concurrent Run + + The function passed to QtConcurrent::run() may report the result + through its return value. + + \section2 Running a Function in a Separate Thread To run a function in another thread, use QtConcurrent::run(): @@ -62,7 +74,7 @@ \snippet code/src_concurrent_qtconcurrentrun.cpp explicit-pool-0 - \section1 Passing Arguments to the Function + \section2 Passing Arguments to the Function Passing arguments to the function is done by adding them to the QtConcurrent::run() call immediately after the function name. For example: @@ -74,7 +86,7 @@ the function. Changes made to the arguments after calling QtConcurrent::run() are \e not visible to the thread. - \section1 Returning Values from the Function + \section2 Returning Values from the Function Any return value from the function is available via QFuture: @@ -88,9 +100,9 @@ to become available. Use QFutureWatcher to get notification when the function has finished execution and the result is available. - \section1 Additional API Features + \section2 Additional API Features - \section2 Using Member Functions + \section3 Using Member Functions QtConcurrent::run() also accepts pointers to member functions. The first argument must be either a const reference or a pointer to an instance of @@ -107,7 +119,7 @@ \snippet code/src_concurrent_qtconcurrentrun.cpp 5 - \section2 Using Lambda Functions + \section3 Using Lambda Functions Calling a lambda function is done like this: @@ -120,6 +132,86 @@ Using callable object is done like this: \snippet code/src_concurrent_qtconcurrentrun.cpp 8 + + \section1 Concurrent Run With Promise + + The QtConcurrent::runWithPromise() enables more control + for the running task comparing to QtConcurrent::run(). + It allows progress reporting of the running task, + reporting multiple results, suspending the execution + if it was requested, or canceling the task on caller's + demand. + + \section2 The mandatory QPromise argument + + The function passed to QtConcurrent::runWithPromise() is expected + to have an additional argument of \e {QPromise &} type, where + T is the type of the computation result (it should match the type T + of QFuture returned by the QtConcurrent::runWithPromise()), like e.g.: + + \snippet code/src_concurrent_qtconcurrentrun.cpp 9 + + The \e promise argument is instantiated inside the + QtConcurrent::runWithPromise() function, and its reference + is passed to the invoked \e aFunction, so the user + doesn't need to instantiate it by himself, nor pass it explicitly + when calling QtConcurrent::runWithPromise(). + + The additional argument of QPromise type always needs to appear + as a first argument on function's arguments list, like: + + \snippet code/src_concurrent_qtconcurrentrun.cpp 10 + + \section2 Reporting results + + In contrast to QtConcurrent::run(), the function passed to + QtConcurrent::runWithPromise() is expected to always return void type. + Result reporting is done through the additional argument of QPromise type. + It also enables multiple result reporting, like: + + \snippet code/src_concurrent_qtconcurrentrun.cpp 11 + + \section2 Suspending and canceling the execution + + The QPromise API also enables suspending and canceling the computation, if requested: + + \snippet code/src_concurrent_qtconcurrentrun.cpp 12 + + The call to \e future.suspend() requests the running task to + hold its execution. After calling this method, the running task + will suspend after the next call to \e promise.suspendIfRequested() + in its iteration loop. In this case the running task will + block on a call to \e promise.suspendIfRequested(). The blocked + call will unblock after the \e future.resume() is called. + Note, that internally suspendIfRequested() uses wait condition + in order to unblock, so the running thread goes into an idle state + instead of wasting its resources when blocked in order to periodically + check if the resume request came from the caller's thread. + + The call to \e future.cancel() from the last line causes that the next + call to \e promise.isCanceled() will return \c true and + \e aFunction will return immediately without any further result reporting. + + \section2 Progress reporting + + It's also possible to report the progress of a task + independently of result reporting, like: + + \snippet code/src_concurrent_qtconcurrentrun.cpp 13 + + The caller installs the \e QFutureWatcher for the \e QFuture + returned by QtConcurrent::runWithPromise() in order to + connect to its \e progressValueChanged() signal and update + e.g. the graphical user interface accordingly. + + \section2 Invoking functions with overloaded operator()() + + By default, QtConcurrent::runWithPromise() doesn't support functors with + overloaded operator()(). In case of overloaded functors the user + needs to explicitly specify the result type + as a template parameter passed to runWithPromise, like: + + \snippet code/src_concurrent_qtconcurrentrun.cpp 14 */ /*! @@ -172,3 +264,52 @@ \sa {Concurrent Run} */ + +/*! + \since 6.0 + \fn QFuture QtConcurrent::runWithPromise(Function function, ...); + + Equivalent to + \code + QtConcurrent::runWithPromise(QThreadPool::globalInstance(), function, ...); + \endcode + + Runs \a function in a separate thread. The thread is taken from the global + QThreadPool. Note that \a function may not run immediately; \a function + will only be run once a thread becomes available. + + The \a function is expected to return void + and must take an additional argument of \e {QPromise &} type, + placed as a first argument in function's argument list. T is the result type + and it is the same for the returned \e QFuture. + + Similar to QtConcurrent::run(), the QFuture returned can be used to query for the + running/finished status and the value reported by the function. In addition, + it may be used for suspending or canceling the running task, fetching + multiple results from the called /a function or monitoring progress + reported by the \a function. + + \sa {Concurrent Run With Promise} +*/ + +/*! + \since 6.0 + \fn QFuture QtConcurrent::runWithPromise(QThreadPool *pool, Function function, ...); + + Runs \a function in a separate thread. The thread is taken from the + QThreadPool \a pool. Note that \a function may not run immediately; \a function + will only be run once a thread becomes available. + + The \a function is expected to return void + and must take an additional argument of \e {QPromise &} type, + placed as a first argument in function's argument list. T is the result type + and it is the same for the returned \e QFuture. + + Similar to QtConcurrent::run(), the QFuture returned can be used to query for the + running/finished status and the value reported by the function. In addition, + it may be used for suspending or canceling the running task, fetching + multiple results from the called /a function or monitoring progress + reported by the \a function. + + \sa {Concurrent Run With Promise} +*/ diff --git a/src/concurrent/qtconcurrentrun.h b/src/concurrent/qtconcurrentrun.h index 2f9b1b5e96..5804393e25 100644 --- a/src/concurrent/qtconcurrentrun.h +++ b/src/concurrent/qtconcurrentrun.h @@ -61,6 +61,12 @@ namespace QtConcurrent { template QFuture run(QThreadPool *pool, Function function, ...); + template + QFuture runWithPromise(Function function, ...); + + template + QFuture runWithPromise(QThreadPool *pool, Function function, ...); + } // namespace QtConcurrent #else -- cgit v1.2.3