QtConcurrent: add fluent interface to configure a task before run
Task-number: QTBUG-82950 Change-Id: I449da938b6b501a7646b3425edde5c880d6ca87e Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Paul Wicking <paul.wicking@qt.io> Reviewed-by: Mikhail Svetkin <mikhail.svetkin@gmail.com>
This commit is contained in:
parent
d975ad4ed7
commit
5a0d4f3313
@ -22,6 +22,8 @@ qt_add_module(Concurrent
|
||||
qtconcurrentrunbase.h
|
||||
qtconcurrentstoredfunctioncall.h
|
||||
qtconcurrentthreadengine.cpp qtconcurrentthreadengine.h
|
||||
qtaskbuilder.h
|
||||
qtconcurrenttask.h
|
||||
DEFINES
|
||||
QT_NO_FOREACH
|
||||
QT_NO_USING_NAMESPACE
|
||||
|
@ -15,7 +15,7 @@ SOURCES += \
|
||||
qtconcurrentmap.cpp \
|
||||
qtconcurrentrun.cpp \
|
||||
qtconcurrentthreadengine.cpp \
|
||||
qtconcurrentiteratekernel.cpp \
|
||||
qtconcurrentiteratekernel.cpp
|
||||
|
||||
HEADERS += \
|
||||
qtconcurrent_global.h \
|
||||
@ -32,7 +32,9 @@ HEADERS += \
|
||||
qtconcurrentrun.h \
|
||||
qtconcurrentrunbase.h \
|
||||
qtconcurrentstoredfunctioncall.h \
|
||||
qtconcurrentthreadengine.h
|
||||
qtconcurrentthreadengine.h \
|
||||
qtaskbuilder.h \
|
||||
qtconcurrenttask.h
|
||||
|
||||
# private headers
|
||||
HEADERS += \
|
||||
|
@ -0,0 +1,127 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the documentation of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//! [0]
|
||||
QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn();
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
auto task = [](const QString &s){ qDebug() << ("Hello, " + s); };
|
||||
QtConcurrent::task(std::move(task))
|
||||
.withArguments("world!")
|
||||
.spawn();
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
QString s("Hello, ");
|
||||
QtConcurrent::task([](QString &s){ s.append("world!"); })
|
||||
.withArguments(std::ref(s))
|
||||
.spawn();
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
auto future = QtConcurrent::task([]{ return 42; }).spawn();
|
||||
auto result = future.result(); // result == 42
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
std::is_invocable_v<std::decay_t<Task>, std::decay_t<Args>...>
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
QVariant value(42);
|
||||
auto result = QtConcurrent::task(&qvariant_cast<int>)
|
||||
.withArguments(value)
|
||||
.spawn()
|
||||
.result(); // result == 42
|
||||
//! [5]
|
||||
|
||||
//! [6]
|
||||
QString result("Hello, world!");
|
||||
|
||||
QtConcurrent::task(&QString::chop)
|
||||
.withArguments(&result, 8)
|
||||
.spawn()
|
||||
.waitForFinished(); // result == "Hello"
|
||||
//! [6]
|
||||
|
||||
//! [7]
|
||||
auto result = QtConcurrent::task(std::plus<int>())
|
||||
.withArguments(40, 2)
|
||||
.spawn()
|
||||
.result() // result == 42
|
||||
//! [7]
|
||||
|
||||
//! [8]
|
||||
struct CallableWithState
|
||||
{
|
||||
void operator()(int newState) { state = newState; }
|
||||
|
||||
// ...
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
CallableWithState object;
|
||||
|
||||
QtConcurrent::task(std::ref(object))
|
||||
.withArguments(42)
|
||||
.spawn()
|
||||
.waitForFinished(); // The object's state is set to 42
|
||||
//! [8]
|
||||
|
||||
//! [9]
|
||||
QThreadPool pool;
|
||||
QtConcurrent::task([]{ return 42; }).onThreadPool(pool).spawn();
|
||||
//! [9]
|
||||
|
||||
//! [10]
|
||||
QtConcurrent::task([]{ return 42; }).withPriority(10).spawn();
|
||||
//! [10]
|
@ -79,6 +79,13 @@
|
||||
another thread.
|
||||
\endlist
|
||||
|
||||
\li \l {Concurrent Task}
|
||||
\list
|
||||
\li \l {QtConcurrent::task}{QtConcurrent::task()} creates an instance
|
||||
of QtConcurrent::QTaskBuilder. This object can be used for adjusting
|
||||
parameters and for kicking off a task in a separate thread.
|
||||
\endlist
|
||||
|
||||
\li QFuture represents the result of an asynchronous computation.
|
||||
|
||||
\li QFutureIterator allows iterating through results available via QFuture.
|
||||
|
160
src/concurrent/qtaskbuilder.h
Normal file
160
src/concurrent/qtaskbuilder.h
Normal file
@ -0,0 +1,160 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtConcurrent module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTBASE_QTTASKBUILDER_H
|
||||
#define QTBASE_QTTASKBUILDER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#if !defined(QT_NO_CONCURRENT) || defined(Q_CLANG_QDOC)
|
||||
|
||||
#include <QtConcurrent/qtconcurrentstoredfunctioncall.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef Q_CLANG_QDOC
|
||||
|
||||
namespace QtConcurrent {
|
||||
|
||||
using InvokeResultType = int;
|
||||
|
||||
template <class Task, class ...Args>
|
||||
class QTaskBuilder
|
||||
{
|
||||
public:
|
||||
[[nodiscard]]
|
||||
QFuture<InvokeResultType> spawn();
|
||||
|
||||
template <class ...ExtraArgs>
|
||||
[[nodiscard]]
|
||||
QTaskBuilder<Task, ExtraArgs...> withArguments(ExtraArgs &&...args);
|
||||
|
||||
[[nodiscard]]
|
||||
QTaskBuilder<Task, Args...> &onThreadPool(QThreadPool &newThreadPool);
|
||||
|
||||
[[nodiscard]]
|
||||
QTaskBuilder<Task, Args...> &withPriority(int newPriority);
|
||||
};
|
||||
|
||||
} // namespace QtConcurrent
|
||||
|
||||
#else
|
||||
|
||||
namespace QtConcurrent {
|
||||
|
||||
template <class Task, class ...Args>
|
||||
class QTaskBuilder
|
||||
{
|
||||
public:
|
||||
[[nodiscard]]
|
||||
auto spawn()
|
||||
{
|
||||
return (new StoredFunctionCall<Task, Args...>(std::move(taskWithArgs)))
|
||||
->start(startParameters);
|
||||
}
|
||||
|
||||
template <class ...ExtraArgs>
|
||||
[[nodiscard]]
|
||||
constexpr auto withArguments(ExtraArgs &&...args)
|
||||
{
|
||||
static_assert(std::tuple_size_v<TaskWithArgs> == 1,
|
||||
"This function cannot be invoked if "
|
||||
"arguments have already been passed.");
|
||||
|
||||
static_assert(sizeof...(ExtraArgs) >= 1,
|
||||
"One or more arguments must be passed.");
|
||||
|
||||
// We have to re-create a builder, because the type has changed
|
||||
return QTaskBuilder<Task, ExtraArgs...>(
|
||||
startParameters,
|
||||
std::get<0>(std::move(taskWithArgs)),
|
||||
std::forward<ExtraArgs>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &onThreadPool(QThreadPool &newThreadPool)
|
||||
{
|
||||
startParameters.threadPool = &newThreadPool;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &withPriority(int newPriority)
|
||||
{
|
||||
startParameters.priority = newPriority;
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected: // Methods
|
||||
constexpr explicit QTaskBuilder(Task &&task, Args &&...arguments)
|
||||
: taskWithArgs{std::forward<Task>(task), std::forward<Args>(arguments)...}
|
||||
{}
|
||||
|
||||
constexpr QTaskBuilder(
|
||||
const TaskStartParameters ¶meters, Task &&task, Args &&...arguments)
|
||||
: taskWithArgs{std::forward<Task>(task), std::forward<Args>(arguments)...}
|
||||
, startParameters{parameters}
|
||||
{}
|
||||
|
||||
private: // Methods
|
||||
// Required for creating a builder from "task" function
|
||||
template <class T>
|
||||
friend constexpr auto task(T &&t);
|
||||
|
||||
// Required for creating a new builder from "withArguments" function
|
||||
template <class T, class ...A>
|
||||
friend class QTaskBuilder;
|
||||
|
||||
private: // Data
|
||||
using TaskWithArgs = DecayedTuple<Task, Args...>;
|
||||
|
||||
TaskWithArgs taskWithArgs;
|
||||
TaskStartParameters startParameters;
|
||||
};
|
||||
|
||||
} // namespace QtConcurrent
|
||||
|
||||
#endif // Q_CLANG_QDOC
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // !defined(QT_NO_CONCURRENT)
|
||||
|
||||
#endif //QTBASE_QTTASKBUILDER_H
|
82
src/concurrent/qtaskbuilder.qdoc
Normal file
82
src/concurrent/qtaskbuilder.qdoc
Normal file
@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the documentation of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:FDL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Free Documentation License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Free
|
||||
** Documentation License version 1.3 as published by the Free Software
|
||||
** Foundation and appearing in the file included in the packaging of
|
||||
** this file. Please review the following information to ensure
|
||||
** the GNU Free Documentation License version 1.3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/*!
|
||||
\class QtConcurrent::QTaskBuilder
|
||||
\inmodule QtConcurrent
|
||||
\brief The QTaskBuilder class is used for adjusting task parameters.
|
||||
\since 6.0
|
||||
|
||||
\ingroup thread
|
||||
|
||||
It's not possible to create an object of this class manually. See
|
||||
\l {Concurrent Task} for more details and usage examples.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <class Task, class ...Args> [[nodiscard]] QFuture<InvokeResultType> QtConcurrent::QTaskBuilder<Task, Args...>::spawn()
|
||||
|
||||
Runs the task in a separate thread and returns a future object immediately.
|
||||
This is a non-blocking call. The task might not start immediately.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <class Task, class ...Args> template <class ...ExtraArgs> [[nodiscard]] QTaskBuilder<Task, ExtraArgs...> QtConcurrent::QTaskBuilder<Task, Args...>::withArguments(ExtraArgs &&...args)
|
||||
|
||||
Sets the arguments \a args the task will be invoked with. The code is ill-formed
|
||||
(causes compilation errors) if:
|
||||
\list
|
||||
\li This function is invoked more than once.
|
||||
\li The arguments count is zero.
|
||||
\endlist
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <class Task, class ...Args> [[nodiscard]] QTaskBuilder<Task, Args...> &QtConcurrent::QTaskBuilder<Task, Args...>::onThreadPool(QThreadPool &newThreadPool)
|
||||
|
||||
Sets the thread pool \a newThreadPool that the task will be invoked on.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <class Task, class ...Args> [[nodiscard]] QTaskBuilder<Task, Args...> &QtConcurrent::QTaskBuilder<Task, Args...>::withPriority(int newPriority)
|
||||
|
||||
Sets the priority \a newPriority that the task will be invoked with.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef InvokeResultType
|
||||
\relates QtConcurrent::QTaskBuilder
|
||||
|
||||
The simplified definition of this type looks like this:
|
||||
\code
|
||||
template <class Task, class ...Args>
|
||||
using InvokeResultType = std::invoke_result_t<std::decay_t<Task>, std::decay_t<Args>...>;
|
||||
\endcode
|
||||
|
||||
The real implementation also contains a compile-time check for
|
||||
whether the task can be invoked with the specified arguments or not.
|
||||
*/
|
@ -72,7 +72,8 @@ template <class Function, class ...Args>
|
||||
auto run(QThreadPool *pool, Function &&f, Args &&...args)
|
||||
{
|
||||
return (new StoredFunctionCall<Function, Args...>(
|
||||
std::forward<Function>(f), std::forward<Args>(args)...))->start(pool);
|
||||
std::forward<Function>(f), std::forward<Args>(args)...))
|
||||
->start(pool);
|
||||
}
|
||||
|
||||
template <class Function, class ...Args>
|
||||
|
@ -69,25 +69,34 @@ struct SelectSpecialization<void>
|
||||
struct Type { typedef Void type; };
|
||||
};
|
||||
|
||||
struct TaskStartParameters
|
||||
{
|
||||
QThreadPool *threadPool = QThreadPool::globalInstance();
|
||||
int priority = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RunFunctionTaskBase : public QFutureInterface<T> , public QRunnable
|
||||
{
|
||||
public:
|
||||
QFuture<T> start()
|
||||
{
|
||||
return start(QThreadPool::globalInstance());
|
||||
return start(TaskStartParameters());
|
||||
}
|
||||
|
||||
QFuture<T> start(QThreadPool *pool)
|
||||
QFuture<T> start(const TaskStartParameters ¶meters)
|
||||
{
|
||||
this->setThreadPool(pool);
|
||||
this->setThreadPool(parameters.threadPool);
|
||||
this->setRunnable(this);
|
||||
this->reportStarted();
|
||||
QFuture<T> theFuture = this->future();
|
||||
pool->start(this, /*m_priority*/ 0);
|
||||
parameters.threadPool->start(this, parameters.priority);
|
||||
return theFuture;
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
QFuture<T> start(QThreadPool *pool) { return start({pool, 0}); }
|
||||
|
||||
void run() override {}
|
||||
virtual void runFunctor() = 0;
|
||||
};
|
||||
|
@ -48,7 +48,6 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
#ifndef Q_QDOC
|
||||
|
||||
namespace QtConcurrent {
|
||||
@ -65,16 +64,20 @@ struct InvokeResult
|
||||
template <class Function, class ...Args>
|
||||
using InvokeResultType = typename InvokeResult<Function, Args...>::Type;
|
||||
|
||||
template <class ...Types>
|
||||
using DecayedTuple = std::tuple<std::decay_t<Types>...>;
|
||||
|
||||
template <class Function, class ...Args>
|
||||
struct StoredFunctionCall : public RunFunctionTask<InvokeResultType<Function, Args...>>
|
||||
{
|
||||
template <class ...Types>
|
||||
using DecayedTuple = std::tuple<std::decay_t<Types>...>;
|
||||
|
||||
StoredFunctionCall(Function &&f, Args &&...args)
|
||||
: data{std::forward<Function>(f), std::forward<Args>(args)...}
|
||||
{}
|
||||
|
||||
StoredFunctionCall(DecayedTuple<Function, Args...> &&_data)
|
||||
: data(std::move(_data))
|
||||
{}
|
||||
|
||||
void runFunctor() override
|
||||
{
|
||||
using Indexes =
|
||||
|
75
src/concurrent/qtconcurrenttask.h
Normal file
75
src/concurrent/qtconcurrenttask.h
Normal file
@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtConcurrent module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTCONCURRENTTASK_H
|
||||
#define QTCONCURRENTTASK_H
|
||||
|
||||
#if !defined(QT_NO_CONCURRENT)
|
||||
|
||||
#include <QtConcurrent/qtaskbuilder.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef Q_CLANG_QDOC
|
||||
|
||||
namespace QtConcurrent {
|
||||
|
||||
template <class Task>
|
||||
[[nodiscard]]
|
||||
QTaskBuilder<Task> task(Task &&task);
|
||||
|
||||
} // namespace QtConcurrent
|
||||
|
||||
#else
|
||||
|
||||
namespace QtConcurrent {
|
||||
|
||||
template <class Task>
|
||||
[[nodiscard]]
|
||||
constexpr auto task(Task &&t) { return QTaskBuilder(std::forward<Task>(t)); }
|
||||
|
||||
} // namespace QtConcurrent
|
||||
|
||||
#endif // Q_CLANG_QDOC
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // !defined(QT_NO_CONCURRENT)
|
||||
|
||||
#endif // QTCONCURRENTTASK_H
|
156
src/concurrent/qtconcurrenttask.qdoc
Normal file
156
src/concurrent/qtconcurrenttask.qdoc
Normal file
@ -0,0 +1,156 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the documentation of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:FDL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Free Documentation License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Free
|
||||
** Documentation License version 1.3 as published by the Free Software
|
||||
** Foundation and appearing in the file included in the packaging of
|
||||
** this file. Please review the following information to ensure
|
||||
** the GNU Free Documentation License version 1.3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/*!
|
||||
\page qtconcurrenttask.html
|
||||
\title Concurrent Task
|
||||
\ingroup thread
|
||||
|
||||
QtConcurrent::task provides an alternative interface for running a
|
||||
task in a separate thread. The return value of the function is made
|
||||
available through the QFuture API.
|
||||
|
||||
If you want to just run a function in a separate thread without adjusting
|
||||
any parameters, use QtConcurrent::run as that lets you write less code.
|
||||
The QtConcurrent::task is designed for cases where you need to perform
|
||||
extra configurations steps.
|
||||
|
||||
This function is a part of the \l {Qt Concurrent} framework.
|
||||
|
||||
\section1 Fluent interface
|
||||
|
||||
The QtConcurrent::task returns an instance of an auxiliary class called
|
||||
QtConcurrent::QTaskBuilder. Normally, you don't need to create an instance
|
||||
of this class manually. The QtConcurrent::QTaskBuilder provides an interface
|
||||
to adjust different task parameters in a chain-like manner. This approach
|
||||
is known as a
|
||||
\l {https://en.wikipedia.org/wiki/Fluent_interface}{fluent interface}.
|
||||
|
||||
You can just set the parameters you need and then kick a task off.
|
||||
In order to finalize the configuration of a task you must invoke
|
||||
QtConcurrent::QTaskBuilder::spawn. This function is non-blocking (i.e.
|
||||
returns a future object immediately), but it's not guaranteed that the
|
||||
task starts immediately. You can use the QFuture and QFutureWatcher classes
|
||||
to monitor the status of the task.
|
||||
|
||||
See more examples and explanations below.
|
||||
|
||||
\section1 Running a task in a separate thread
|
||||
|
||||
To run a function in another thread, use QtConcurrent::QTaskBuilder::spawn:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 0
|
||||
|
||||
This will run a lambda function in a separate thread obtained from
|
||||
the default QThreadPool.
|
||||
|
||||
\section1 Passing arguments to the task
|
||||
|
||||
Invoking a function with arguments is done by passing them to
|
||||
QtConcurrent::QTaskBuilder::withArguments:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 1
|
||||
|
||||
A copy of each argument is made at the point where
|
||||
QtConcurrent::QTaskBuilder::withArguments is called, and these values
|
||||
are passed to the thread when it begins executing the task. Changes made
|
||||
to the arguments after calling QtConcurrent::QTaskBuilder::withArguments
|
||||
are not visible to the thread.
|
||||
|
||||
If you want to run a function that accepts arguments by reference, you
|
||||
should use \l {https://en.cppreference.com/w/cpp/utility/functional/ref}
|
||||
{std::ref/cref} auxiliary functions. These functions create thin wrappers
|
||||
around passed arguments:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 2
|
||||
|
||||
Make sure that all wrapped objects live long enough. It is possible to
|
||||
get undefined behavior if a task outlives the object wrapped by
|
||||
std::ref/cref.
|
||||
|
||||
\section1 Returning values from the task
|
||||
|
||||
You can obtain the result of a task with the QFuture API:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 3
|
||||
|
||||
Note that QFuture::result() is a blocking call, it waits for the
|
||||
result to become available. Use QFutureWatcher to get a notification
|
||||
when the task has finished execution and the result is available.
|
||||
|
||||
In case you want to pass a result to another asynchronous task, you can
|
||||
use QFuture::then() to create a chain of dependent tasks. See the QFuture
|
||||
documentation for more details.
|
||||
|
||||
\section1 Additional API features
|
||||
|
||||
\section2 Using different types of callable objects
|
||||
|
||||
Strictly speaking, you can use any type of tasks and arguments that
|
||||
satisfy the following condition:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 4
|
||||
|
||||
You can use a free function:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 5
|
||||
|
||||
You can use a member function:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 6
|
||||
|
||||
You can use a callable object with an operator():
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 7
|
||||
|
||||
If you want to use an existing callable object, you need to either
|
||||
copy/move it to QtConcurrent::task or wrap it with std::ref/cref:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 8
|
||||
|
||||
\section2 Using custom thread pool
|
||||
|
||||
You can specify a custom thread pool:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 9
|
||||
|
||||
\section2 Setting priority for a task
|
||||
|
||||
You can set the priority for a task:
|
||||
|
||||
\snippet code/src_concurrent_qtconcurrenttask.cpp 10
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Task> [[nodiscard]] QTaskBuilder<Task> QtConcurrent::task(Task &&task);
|
||||
\since 6.0
|
||||
|
||||
Creates an instance of QtConcurrent::QTaskBuilder. This object can be used
|
||||
to adjust some parameters and run \a task in a separate thread.
|
||||
|
||||
\sa {Concurrent Task}, QtConcurrent::QTaskBuilder
|
||||
*/
|
@ -6,3 +6,4 @@ add_subdirectory(qtconcurrentmap)
|
||||
add_subdirectory(qtconcurrentmedian)
|
||||
add_subdirectory(qtconcurrentrun)
|
||||
add_subdirectory(qtconcurrentthreadengine)
|
||||
add_subdirectory(qtconcurrenttask)
|
||||
|
@ -5,5 +5,6 @@ SUBDIRS=\
|
||||
qtconcurrentmap \
|
||||
qtconcurrentmedian \
|
||||
qtconcurrentrun \
|
||||
qtconcurrentthreadengine
|
||||
qtconcurrentthreadengine \
|
||||
qtconcurrenttask
|
||||
|
||||
|
6
tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt
Normal file
6
tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
add_qt_test(tst_qtconcurrenttask
|
||||
SOURCES
|
||||
tst_qtconcurrenttask.cpp
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::Concurrent
|
||||
)
|
@ -0,0 +1,4 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qtconcurrenttask
|
||||
QT = core testlib concurrent
|
||||
SOURCES = tst_qtconcurrenttask.cpp
|
160
tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp
Normal file
160
tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qtconcurrenttask.h>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class tst_QtConcurrentTask : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void taskWithFreeFunction();
|
||||
void taskWithClassMethod();
|
||||
void taskWithCallableObject();
|
||||
void taskWithLambda();
|
||||
void taskWithArguments();
|
||||
void useCustomThreadPool();
|
||||
void setPriority();
|
||||
void adjustAllSettings();
|
||||
};
|
||||
|
||||
using namespace QtConcurrent;
|
||||
|
||||
void tst_QtConcurrentTask::taskWithFreeFunction()
|
||||
{
|
||||
QVariant value(42);
|
||||
|
||||
auto result = task(&qvariant_cast<int>)
|
||||
.withArguments(value)
|
||||
.spawn()
|
||||
.result();
|
||||
|
||||
QCOMPARE(result, 42);
|
||||
}
|
||||
void tst_QtConcurrentTask::taskWithClassMethod()
|
||||
{
|
||||
QString result("foobar");
|
||||
|
||||
task(&QString::chop).withArguments(&result, 3).spawn().waitForFinished();
|
||||
|
||||
QCOMPARE(result, "foo");
|
||||
}
|
||||
void tst_QtConcurrentTask::taskWithCallableObject()
|
||||
{
|
||||
QCOMPARE(task(std::plus<int>())
|
||||
.withArguments(40, 2)
|
||||
.spawn()
|
||||
.result(),
|
||||
42);
|
||||
}
|
||||
|
||||
void tst_QtConcurrentTask::taskWithLambda()
|
||||
{
|
||||
QCOMPARE(task([]{ return 42; }).spawn().result(), 42);
|
||||
}
|
||||
|
||||
void tst_QtConcurrentTask::taskWithArguments()
|
||||
{
|
||||
auto result = task([](int arg1, int arg2){ return arg1 + arg2; })
|
||||
.withArguments(40, 2)
|
||||
.spawn()
|
||||
.result();
|
||||
QCOMPARE(result, 42);
|
||||
}
|
||||
|
||||
void tst_QtConcurrentTask::useCustomThreadPool()
|
||||
{
|
||||
QThreadPool pool;
|
||||
|
||||
int result = 0;
|
||||
task([&]{ result = 42; }).onThreadPool(pool).spawn().waitForFinished();
|
||||
|
||||
QCOMPARE(result, 42);
|
||||
}
|
||||
|
||||
void tst_QtConcurrentTask::setPriority()
|
||||
{
|
||||
QThreadPool pool;
|
||||
pool.setMaxThreadCount(1);
|
||||
|
||||
QSemaphore sem;
|
||||
|
||||
QVector<QFuture<void>> futureResults;
|
||||
futureResults << task([&]{ sem.acquire(); })
|
||||
.onThreadPool(pool)
|
||||
.spawn();
|
||||
|
||||
const int tasksCount = 10;
|
||||
QVector<int> priorities(tasksCount);
|
||||
std::iota(priorities.begin(), priorities.end(), 1);
|
||||
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
std::shuffle(priorities.begin(), priorities.end(), std::default_random_engine(seed));
|
||||
|
||||
QVector<int> actual;
|
||||
for (int priority : priorities)
|
||||
futureResults << task([priority, &actual] { actual << priority; })
|
||||
.onThreadPool(pool)
|
||||
.withPriority(priority)
|
||||
.spawn();
|
||||
|
||||
sem.release();
|
||||
pool.waitForDone();
|
||||
|
||||
for (const auto &f : futureResults)
|
||||
QVERIFY(f.isFinished());
|
||||
|
||||
QVector<int> expected(priorities);
|
||||
std::sort(expected.begin(), expected.end(), std::greater<>());
|
||||
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
void tst_QtConcurrentTask::adjustAllSettings()
|
||||
{
|
||||
QThreadPool pool;
|
||||
pool.setMaxThreadCount(1);
|
||||
|
||||
const int priority = 10;
|
||||
|
||||
QVector<int> result;
|
||||
auto append = [&](auto &&...args){ (result << ... << args); };
|
||||
|
||||
task(std::move(append))
|
||||
.withArguments(1, 2, 3)
|
||||
.onThreadPool(pool)
|
||||
.withPriority(priority)
|
||||
.spawn()
|
||||
.waitForFinished();
|
||||
|
||||
QCOMPARE(result, QVector<int>({1, 2, 3}));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QtConcurrentTask)
|
||||
#include "tst_qtconcurrenttask.moc"
|
Loading…
x
Reference in New Issue
Block a user