Задача ввода-вывода
QtConcurrent::task предоставляет альтернативный интерфейс для запуска задачи в отдельном потоке. Значение возвращаемого функцией результата доступно через API QFuture.
Если вам нужно просто запустить функцию в отдельном потоке без изменения каких-либо параметров, используйте QtConcurrent::run, так как это позволяет написать меньше кода. QtConcurrent::task предназначен для случаев, когда вам необходимо выполнить дополнительные шаги настройки.
Эта функция является частью фреймворка Qt Concurrent.
Последовательный интерфейс
QtConcurrent::task возвращает экземпляр вспомогательного класса, называемого QtConcurrent::QTaskBuilder. Как правило, вам не нужно создавать экземпляр этого класса вручную. QtConcurrent::QTaskBuilder предоставляет интерфейс для настройки различных параметров задачи цепочечным способом. Этот подход известен как последовательный интерфейс.
Вы можете просто установить необходимые параметры, а затем запустить задачу. Для завершения настройки задачи необходимо вызвать QtConcurrent::QTaskBuilder::spawn. Эта функция неблокирующая (т.е. возвращает объект будущего немедленно), но нет гарантии, что задача начнется немедленно. Вы можете использовать классы 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(); Если вам не нужен объект будущего, вы можете вызвать 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.2/qtconcurrenttask.html