Spec-Zone.ru › Qt

Многопоточные технологии в 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(), чтобы получить возвращаемое значение функции.
Один вызов Выполнение операции со всеми элементами контейнера, используя все доступные ядра. Например, создание миниатюр из списка изображений. Используйте функцию QtConcurrent::filter() Qt Concurrent для выбора элементов контейнера, а функцию 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-6.2/threads-technologies.html

Spec-Zone.ru

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