Spec-Zone.ru › Qt 6.0

Многопоточные технологии в 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, а также рекомендуемые решения для некоторых примеров использования.

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

ХарактеристикаQThreadQRunnable и QThreadPoolQtConcurrent::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, обновляя пользовательский интерфейс, когда результаты будут готовы.Разместите код вычисления в .js скрипте и прикрепите его к экземпляру WorkerScript. Вызовите WorkerScript.sendMessage(), чтобы запустить вычисление в новом потоке. Пусть скрипт также вызовет 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-6.0/threads-technologies.html

Spec-Zone.ru

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