Задача в параллельной среде
QtConcurrent::task предоставляет альтернативный интерфейс для выполнения задачи в отдельном потоке. Возвращаемое значение функции доступно через API QFuture.
Если вам нужно просто запустить функцию в отдельном потоке без изменения параметров, используйте QtConcurrent::run, так как это позволит сократить объём кода. QtConcurrent::task предназначен для случаев, когда требуется выполнение дополнительных конфигурационных шагов.
Эта функция является частью фреймворка Qt Concurrent.
Плавно настраиваемый интерфейс
QtConcurrent::task возвращает экземпляр вспомогательного класса, называемого QtConcurrent::QTaskBuilder. Как правило, вам не нужно создавать экземпляр этого класса вручную. QtConcurrent::QTaskBuilder предоставляет интерфейс для настройки различных параметров задачи в цепочечном стиле. Этот подход известен как плавный интерфейс.
Вы можете просто установить необходимые параметры, а затем запустить задачу. Для завершения настройки задачи необходимо вызвать QtConcurrent::QTaskBuilder::spawn. Эта функция является неблокирующей (т.е. возвращает объект future сразу), но нет гарантии, что задача запустится немедленно. Вы можете использовать классы QFuture и QFutureWatcher для мониторинга статуса задачи.
См. дополнительные примеры и пояснения ниже.
Запуск задачи в отдельном потоке
Для запуска функции в другом потоке используйте QtConcurrent::QTaskBuilder::spawn:
QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn(); Это запустит лямбда-функцию в отдельном потоке, полученном из стандартного QThreadPool.
Передача аргументов задаче
Вызов функции с аргументами выполняется путём передачи их в QtConcurrent::QTaskBuilder::withArguments:
auto task = [](const QString &s){ qDebug() << ("Hello, " + s); };
QtConcurrent::task(std::move(task))
.withArguments("world!")
.spawn(); Копия каждого аргумента создаётся в момент вызова QtConcurrent::QTaskBuilder::withArguments, и эти значения передаются в поток при запуске выполнения задачи. Изменения, внесённые в аргументы после вызова QtConcurrent::QTaskBuilder::withArguments, не видны потоку.
Если вы хотите запустить функцию, которая принимает аргументы по ссылке, используйте вспомогательные функции std::ref/cref. Эти функции создают тонкие оболочки вокруг переданных аргументов:
QString s("Hello, ");
QtConcurrent::task([](QString &s){ s.append("world!"); })
.withArguments(std::ref(s))
.spawn(); Убедитесь, что все обернутые объекты существуют достаточно долго. Возможны неопределённые результаты, если задача переживёт объект, обернутый std::ref/cref.
Возврат значений из задачи
Вы можете получить результат задачи с помощью API QFuture:
auto future = QtConcurrent::task([]{ return 42; }).spawn();
auto result = future.result(); // result == 42 Обратите внимание, что QFuture::result() — это блокирующий вызов; он ожидает, пока результат не станет доступным. Используйте QFutureWatcher, чтобы получить уведомление о завершении выполнения задачи и о доступности результата.
Если вы хотите передать результат в другую асинхронную задачу, вы можете использовать QFuture::then(), чтобы создать цепочку зависимых задач. Дополнительные сведения см. в документации к QFuture.
Дополнительные возможности API
Использование различных типов вызываемых объектов
Строго говоря, вы можете использовать любой тип задач и аргументов, удовлетворяющих следующему условию:
std::is_invocable_v<std::decay_t<Task>, std::decay_t<Args>...>
Вы можете использовать свободную функцию:
QVariant value(42);
auto result = QtConcurrent::task(&qvariant_cast<int>)
.withArguments(value)
.spawn()
.result(); // result == 42 Вы можете использовать член-функцию:
QString result("Hello, world!");
QtConcurrent::task(&QString::chop)
.withArguments(&result, 8)
.spawn()
.waitForFinished(); // result == "Hello" Вы можете использовать вызываемый объект с оператором ():
auto result = QtConcurrent::task(std::plus<int>())
.withArguments(40, 2)
.spawn()
.result() // result == 42 Если вы хотите использовать существующий вызываемый объект, вам нужно либо скопировать/переместить его в QtConcurrent::task, либо обернуть его с помощью std::ref/cref:
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 Использование пользовательского пула потоков
Вы можете указать пользовательский пул потоков:
QThreadPool pool;
QtConcurrent::task([]{ return 42; }).onThreadPool(pool).spawn(); Установка приоритета для задачи
Вы можете установить приоритет для задачи:
QtConcurrent::task([]{ return 42; }).withPriority(10).spawn(); Если вам не нужен объект future, вы можете вызвать QtConcurrent::QTaskBuilder::spawn(QtConcurrent::FutureResult::Ignore):
QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn(FutureResult::Ignore); Вы можете получить доступ к объекту обещания, связанному с задачей, определив дополнительный аргумент типа QPromise<T> & внутри функции. Этот дополнительный аргумент должен быть первым аргументом, переданным в функцию, и, как и в режиме Concurrent Run With Promise, функция должна возвращать тип void. Сообщения о результатах осуществляются через API QPromise:
void increment(QPromise<int> &promise, int i)
{
promise.addResult(i + 1);
}
int result = QtConcurrent::task(&increment).withArguments(10).spawn().result(); // result == 11
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.0/qtconcurrenttask.html