Spec-Zone.ru › Qt 5.6

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

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

Функция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 приложении и обновить GUI, когда результаты будут готовы.Разместите код вычисления в .js скрипте и прикрепите его к экземпляру WorkerScript. Вызовите sendMessage(), чтобы начать вычисление в новом потоке. Позвольте скрипту также вызвать WorkerScript::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/archives/qt-5.6/threads-technologies.html

Spec-Zone.ru

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