Spec-Zone.ru › Qt 6.0

Задача в параллельной среде

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

Spec-Zone.ru

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