Spec-Zone.ru › Qt 5.13

Технологии многопоточности в 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 работать параллельно с потоком GUI.

Каждый экземпляр WorkerScript может иметь один .js скрипт. При вызове WorkerScript.sendMessage() скрипт будет выполняться в отдельном потоке (и в отдельном QML контексте). После завершения выполнения скрипта он может отправить ответ обратно в поток GUI, который вызовет обработчик сигнала 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 QtConcurrent::filter() для выбора элементов контейнера и функцию QtConcurrent::map() для применения операции к каждому элементу. Для сворачивания выходных данных в один результат, используйте QtConcurrent::filteredReduced() и QtConcurrent::mappedReduced() вместо этого.
Один вызов/Постоянный Выполнение длительных вычислений в чистом приложении QML и обновление GUI при готовности результатов. Разместите код вычислений в .js скрипте и прикрепите его к экземпляру WorkerScript. Вызовите WorkerScript.sendMessage() для запуска вычислений в новом потоке. Пусть скрипт также вызовет sendMessage(), чтобы передать результат обратно в поток GUI. Обработайте результат в onMessage и обновите GUI там.
Постоянный Иметь объект, живущий в другом потоке, который может выполнять различные задачи по запросу и/или получать новые данные для работы. Создайте подкласс QObject для создания рабочего объекта. Создайте этот рабочий объект и QThread. Переместите рабочий объект в новый поток. Отправляйте команды или данные рабочему объекту через связанные сигналы-слоты.
Постоянный Повторяющееся выполнение дорогостоящей операции в другом потоке, где поток не должен получать никаких сигналов или событий. Напишите бесконечный цикл непосредственно в переопределении QThread::run(). Запустите поток без цикла событий. Пусть поток отправляет сигналы для отправки данных обратно в поток GUI.

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

Spec-Zone.ru

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