Spec-Zone.ru › Qt 5.9

Многопоточные технологии в Qt

Qt предлагает множество классов и функций для работы с потоками. Ниже приведены четыре различных подхода, которые разработчики Qt могут использовать для реализации многопоточных приложений.

QThread: Базовый API с необязательными циклами событий

QThread является основой всего управления потоками в Qt. Каждый экземпляр QThread представляет и управляет одним потоком.

QThread может быть создан напрямую или как подкласс. Создание QThread предоставляет параллельный цикл событий, позволяющий вызывать слоты QObject во вторичном потоке. Наследование QThread позволяет приложению инициализировать новый поток перед запуском его цикла событий или выполнять параллельный код без цикла событий.

См. справочную информацию по классу QThread и примеры потоков для демонстрации использования QThread.

QThreadPool и QRunnable: Переиспользование потоков

Частое создание и уничтожение потоков может быть дорогостоящим. Чтобы уменьшить эти накладные расходы, существующие потоки можно повторно использовать для новых задач. QThreadPool — это коллекция переиспользуемых QThreads.

Для выполнения кода в одном из потоков QThreadPool переопределите QRunnable::run() и создайте экземпляр подкласса QRunnable. Используйте QThreadPool::start() для помещения QRunnable в очередь выполнения QThreadPool. Когда поток становится доступным, код внутри QRunnable::run() будет выполняться в этом потоке.

В каждом приложении Qt есть глобальный пул потоков, к которому можно получить доступ через QThreadPool::globalInstance(). Этот глобальный пул потоков автоматически поддерживает оптимальное количество потоков, основанное на количестве ядер в процессоре. Однако можно создать и явно управлять отдельным QThreadPool.

Qt Concurrent: Использование API высокого уровня

Модуль Qt Concurrent предоставляет функции высокого уровня, которые обрабатывают некоторые распространенные шаблоны параллельных вычислений: map, filter и reduce. В отличие от использования QThread и QRunnable, эти функции никогда не требуют использования базовых примитивов потоков, таких как мьютексы или семафоры. Вместо этого они возвращают объект QFuture, который можно использовать для получения результатов функций, когда они будут готовы. QFuture также можно использовать для запроса прогресса вычислений и приостановки/возобновления/отмены вычислений. Для удобства QFutureWatcher позволяет взаимодействовать с QFuture через сигналы и слоты.

Алгоритмы map, filter и reduce в Qt Concurrent автоматически распределяют вычисления по всем доступным ядрам процессора, поэтому приложения, написанные сегодня, будут продолжать масштабироваться при развертывании на системах с большим количеством ядер в будущем.

Этот модуль также предоставляет функцию QtConcurrent::run(), которая может запускать любую функцию в другом потоке. Однако QtConcurrent::run() поддерживает только подмножество функций, доступных для map, filter и reduce. QFuture можно использовать для получения возвращаемого значения функции и проверки того, запущен ли поток. Однако вызов QtConcurrent::run() использует только один поток, не может быть приостановлен/возобновлен/отменён и не может быть запрошен прогресс.

См. документацию по модулю Qt Concurrent для получения подробной информации об отдельных функциях.

WorkerScript: Потоки в QML

Тип QML WorkerScript позволяет коду JavaScript выполняться параллельно с потоком графического интерфейса.

У каждого экземпляра WorkerScript может быть один .js скрипт. При вызове WorkerScript::sendMessage() скрипт будет выполняться в отдельном потоке (и в отдельном QML контексте). После завершения выполнения скрипта он может отправить ответ обратно в поток графического интерфейса, который вызовет обработчик сигнала WorkerScript::onMessage().

Использование WorkerScript аналогично использованию объекта-работника QObject, который был перемещен в другой поток. Данные передаются между потоками через сигналы.

См. документацию по WorkerScript для получения подробной информации о реализации скрипта и списка типов данных, которые могут передаваться между потоками.

Выбор подходящего подхода

Как показано выше, Qt предоставляет различные решения для разработки многопоточных приложений. Правильное решение для конкретного приложения зависит от назначения нового потока и срока его жизни. Ниже приводится сравнение многопоточных технологий Qt, а также рекомендуемые решения для некоторых примеров использования.

Сравнение решений

Функция QThread QRunnable и QThreadPool QtConcurrent::run() Qt Concurrent (Map, Filter, Reduce) WorkerScript
Язык C++ C++ C++ C++ QML
Можно указать приоритет потока Да Да
Поток может запускать цикл событий Да
Поток может получать обновления данных через сигналы Да (получается объектом-работником QObject) Да (получается WorkerScript)
Поток можно управлять с помощью сигналов Да (получается QThread) Да (получается QFutureWatcher)
Поток можно отслеживать с помощью QFuture Частично Да
Встроенная возможность приостановки/возобновления/отмены Да

Примеры использования

Срок жизни потока Операция Решение
Один вызов Выполнить новую линейную функцию в другом потоке, с необязательными обновлениями прогресса во время выполнения. Qt предоставляет различные решения:
  • Поместите функцию в переопределение QThread::run() и запустите QThread. Выдавайте сигналы для обновления прогресса. ИЛИ
  • Поместите функцию в переопределение QRunnable::run() и добавьте QRunnable в QThreadPool. Записывайте в защищенную от потоков переменную для обновления прогресса. ИЛИ
  • Запустите функцию с помощью QtConcurrent::run(). Записывайте в защищенную от потоков переменную для обновления прогресса.
Один вызов Выполнить существующую функцию в другом потоке и получить ее возвращаемое значение. Запустите функцию с помощью QtConcurrent::run(). Используйте QFutureWatcher для срабатывания сигнала finished(), когда функция вернет результат, и вызовите QFutureWatcher::result() для получения возвращаемого значения функции.
Один вызов Выполнить операцию со всеми элементами контейнера, используя все доступные ядра. Например, создание миниатюр из списка изображений. Используйте функцию Qt Concurrent's QtConcurrent::filter() для выбора элементов контейнера и функцию QtConcurrent::map() для применения операции к каждому элементу. Для свёртки вывода в один результат используйте QtConcurrent::filteredReduced() и QtConcurrent::mappedReduced().
Один вызов/Постоянный Выполнить длительные вычисления в чисто QML приложении и обновить графический интерфейс, когда результаты будут готовы. Поместите код вычислений в .js скрипт и прикрепите его к экземпляру WorkerScript. Вызовите sendMessage() для запуска вычислений в новом потоке. Позвольте скрипту также вызвать WorkerScript::sendMessage() для передачи результата обратно в поток графического интерфейса. Обработайте результат в onMessage и обновите графический интерфейс там.
Постоянный Иметь объект, живущий в другом потоке, который может выполнять различные задачи по запросу и/или может получать новые данные для работы. Создайте подкласс QObject для создания объекта-работника. Создайте этот объект-работник и QThread. Переместите объект-работник в новый поток. Отправляйте команды или данные в объект-работник через очереди сигналов-слотов.
Постоянный Повторять дорогостоящую операцию в другом потоке, где поток не нуждается в получении сигналов или событий. Напишите бесконечный цикл непосредственно в переопределении QThread::run(). Запустите поток без цикла событий. Позвольте потоку выдавать сигналы для отправки данных обратно в поток графического интерфейса.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.9/threads-technologies.html

Spec-Zone.ru

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