moveToThread по сравнению с QThread в Qt

Когда следует moveToThread предпочтительнее, чем подкласс QThread?

Эта ссылка показывает, что оба метода работают. На каком основании я должен решить, что использовать из этих двух?

Ответ 1

Я бы сосредоточился на различиях между этими двумя методами. Существует не общий ответ, который подходит для всех случаев использования, поэтому хорошо понимать, что именно они должны выбрать, что подходит вашему делу.

Использование moveToThread()

moveToThread() используется для управления привязкой к потоку объектов, что в основном означает настройку потока (или, лучше, цикла событий Qt), из которого объект будет излучать сигналы, и его слоты будут выполнены.

Как показано в документации, которую вы связали, ее можно использовать для запуска кода в другом потоке, в основном создавая манекена, записывая код для запуска в общедоступном слоте (в примере слот doWork()), а затем используя moveToThread для переместите его в другой цикл событий.

Затем загорается сигнал, подключенный к этому слоту. Поскольку объект, который испускает сигнал (контроллер в примере), живет в другом потоке, и сигнал подключен к нашему методу doWork с подключением по очереди, метод doWork будет выполнен в рабочем потоке.

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

Подкласс QThread()

Другим способом, описанным в документации Qt, является подкласс QThread. В этом случае один переопределяет стандартную реализацию метода QThread :: run(), который создает цикл событий, для запуска чего-то еще.

В этом подходе нет ничего плохого, хотя есть несколько уловов.

Прежде всего, очень легко написать небезопасный код, потому что метод run() является единственным в этом классе, который будет фактически запущен в другом потоке.

Если в качестве примера у вас есть переменная-член, которую вы инициализируете в конструкторе, а затем используете в методе run(), ваш член инициализируется в потоке вызывающего, а затем используется в новом потоке.

Такая же история для любого общедоступного метода, который может быть вызван либо из вызывающего, либо внутри run().

Также слоты будут выполняться из потока вызывающего абонента (если вы не сделаете что-то действительно странное, как moveToThread (this)), что приведет к дополнительной путанице.

Таким образом, это возможно, но вы действительно сами по себе с этим подходом, и вы должны уделять дополнительное внимание.

Другие подходы

Конечно, есть альтернативы для обоих подходов, в зависимости от того, что вам нужно. Если вам просто нужно запустить некоторый код в фоновом режиме во время работы потока GUI, вы можете использовать QtConcurrent :: run().

Однако имейте в виду, что QtConcurrent будет использовать глобальный QThreadPool. Если весь пул занят (это означает, что в пуле нет доступных потоков), ваш код не будет запущен немедленно.

Другой альтернативой, если вы хотя бы на С++ 11, является использование API нижнего уровня, такого как std :: thread.

Ответ 2

В качестве отправной точки: не используйте ни один из них. В большинстве случаев у вас есть единица работы, которую вы хотите запустить асинхронно. Для этого используйте QtConcurrent::run.

Если у вас есть объект, который реагирует на события и/или использует таймеры, это QObject который должен быть неблокирующим и идти в потоке, возможно, совместно с другими объектами.

Такой объект может также блокировать блокирующие API.

Подклассификация QThread никогда не требуется на практике. Это похоже на подкласс QFile. QThread - это дескриптор потока. Он обертывает системный ресурс. Перегрузка это немного глупо.

Ответ 3

QThread - это абстракция потока низкого уровня, сначала посмотрите на модуль API QtConcurrent высокого уровня и QRunnable

Если ничто из этого не подходит для вас, прочитайте эту старую статью, в которой рассказывается, как использовать QThread. Подумайте о потоке и задаче, выполняемой в этом потоке как отдельные объекты, не смешивайте их вместе.

Таким образом, если вам нужно написать на заказ, конкретную или расширенную обертку потоков, тогда вы должны подклассифицировать QThread.

Если у вас есть производный класс QObject с сигналами и слотами, используйте на нем moveToThread.

В других случаях используйте QtConcurrent, QRunnable и QThreadPoll.

Ответ 4

Простой ответ - ВСЕГДА. Когда вы перемещаете объект в поток:

  • легко написать тест для кода
  • это легко для кода рефакторинга (вы можете использовать поток, но вам не нужно).
  • вы не смешиваете функциональность потока с бизнес-логикой
  • нет проблемы с временем жизни объекта

Когда вы подклассифицируете QThread

  • сложнее написать тест
  • процесс очистки объекта может очень запутывать, приводя к странным ошибкам.

Существует полное описание проблемы из блога Qt: Youre делает это неправильно....

QtConcurrent::run также очень удобен.

Помните, что по умолчанию слоты пытаются перепрыгнуть между протекторами, когда посылается сигнал от другого объекта потока. Подробнее см. Документацию Qt :: ConnectionType.