Однако, как только рабочий поток завершается, привязка потока QObject больше не должна быть действительной.
Рабочий поток НЕ завершается после вызова вашей функции. Весь смысл использования QtConcurrent::run
заключается в выполнении большого количества небольших задач на глобальном пул потоков (или некоторые предоставленные QThreadPool
), в то время как повторное использование потоков, чтобы избежать накладных расходов на создание и уничтожение потоков для каждой из этих небольших задач. Помимо распределения вычислений по всем доступным ядрам.
Вы можете попробовать просмотреть исходный код Qt, чтобы увидеть как реализовано QtConcurrent::run
. Вы увидите, что он в конечном итоге вызывает RunFunctionTaskBase::start
, который фактически вызывает QThreadPool::start
с QRunnable
, который вызывает функция, которая изначально была передана в QtConcurrent::run
.
Теперь я хочу перейти к тому, что QThreadPool::start
это реализуется добавлением QRunnable
в очередь, а затем попыткой разбудить один из потоков из пула потоков (которые являются ожидание добавления нового QRunnable
в очередь). Здесь следует отметить, что потоки из пула потоков не запускают цикл событий (они не предназначены для этого), они просто выполняют QRunnable
в очереди и не более того (они реализованы таким образом для причины производительности очевидно).
Это означает, что в тот момент, когда вы создаете QObject
в функции, выполняемой в QtConcurrent::run
, вы просто создаете QObject
, который живет в потоке без цикла событий, из документы, ограничения включают:
Если цикл событий не запущен, события не будут доставлены в объект. Например, если вы создаете объект QTimer
в потоке, но никогда не вызываете exec()
, QTimer
никогда не выдаст свой сигнал timeout()
. Позвонить deleteLater()
тоже не получится. (Эти ограничения применяются и к основному потоку.)
TL;DR: QtConcurrent::run
запускает функции в потоках из глобального QThreadPool
(или предоставленного). Эти потоки не запускают цикл событий, они просто ждут запуска QRunnable
s. Таким образом, QObject
, живущий в потоке из этих потоков, не получает никаких событий.
В документации они использовали < a href="https://doc.qt.io/qt-5/threads-technologies.html#qthread-low-level-api-with-Optional-event-loops" rel="nofollow">QThread
(возможно, с циклом событий и рабочим объектом) и используя QtConcurrent::run
как две отдельные многопоточные технологии. Они не предназначены для смешивания вместе. Итак, в пулах потоков нет рабочих объектов, это просто напрашивается на неприятности.
Вопрос: Qt автоматически перемещает объекты QObject в родительский поток, или мы несем ответственность за перемещение их в допустимый поток до завершения рабочего потока?
Я думаю, что, посмотрев на вещи таким образом, ответ очевиден, что Qt НЕ автоматически перемещает QObject
s в любой поток. Документация предупреждает об использовании QObject
в QThread
без цикла обработки событий, вот и все.
Вы можете свободно перемещать их в любую ветку, которая вам нравится. Но имейте в виду, что moveToThread()
иногда может вызывать проблемы. Например, если перемещение рабочего объекта включает перемещение QTimer
:
Обратите внимание, что все активные таймеры объекта будут сброшены. Таймеры сначала останавливаются в текущем потоке и перезапускаются (с тем же интервалом) в targetThread. В результате постоянное перемещение объекта между потоками может откладывать события таймера на неопределенное время.
Вывод: я думаю, что вам следует подумать об использовании собственного QThread
, который запускает свой цикл обработки событий, и создать там свои рабочие QObject
вместо использования QtConcurrent
. Этот способ намного лучше, чем перемещение QObject
, и позволяет избежать многих ошибок, которые могут возникнуть при использовании вашего текущего подхода. Взгляните на сравнительную таблицу многопоточных технологий в Qt и выберите технологию, наиболее подходящую для вашего варианта использования. Используйте QtConcurrent
только в том случае, если вы хотите просто выполнить функцию с одним вызовом и получить ее возвращаемое значение. Если вы хотите постоянно взаимодействовать с потоком, вам следует переключиться на использование собственного QThread
с рабочими QObject
s.
person
Mike
schedule
14.10.2016