Spec-Zone.ru › Qt 6.1

Задача ввода-вывода

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API