A
Artisan22 августа 2020 г. 13:37

И вновь о потоках и moveToThread.

Qt

Для создания потоков я успешно использую подход moveToThread. Как и принято, при таком подходе, конструктор класса оставляем пустым (дабы не забываем что наш объект, который должен переместиться в поток, будет таки первоначально создан в основном (GUI) потоке. А всё что должно было быть выполнено конструктором, выполняем по сигналу &QThread::started. И на первый взгляд можно подумать, что теперь наш MyObj расположен в новом потоке, и далее можно о нём забыть и не беспокоиться. И действительно, все поля нашего объекта будут расположены в новом потоке. А вот теперь момент, на который в статьях почти нет никакого акцента. Где же будут выполняться методы, слоты MyObj который мы поместили в NewThread? После такого простого и действенного способа как moveToThread так и хочется подумать что будут выполняться в NewThread, и при использовании в QML, при реакции на какой-нибудь onClicked: MyObj.some_slot (допустим MyObj зарегистрировав через rootContext(), хотя можно и плагином через import, в вопросе потоков разницы никакой), мы можем и не ожидать что наш слот/метод всё равно будет проворачиваться в (GUI) потоке, а не как можно наскоро предположить, что во NewThread.
Для решения данной проблемы мне приходиться использовать класс посредника, Mediator, который так и остаётся "жить в GUI потоке", именно его мы регистрируем в QML контексте, и создаём цепочку через onClicked: Meditator.some_signal. И вот уже на стороне Mediator мы можем указать где выполнить слот MyObj, положив его в очередь используя Qt::ConnectionType::QueuedConnection или Qt::ConnectionType::AutoConnection.
Вот собственно и вопрос, есть ли способ на стороне QML сразу указывать что слот должен выполниться на стороне потока своего объекта, или еще каким-то образом обязать все методы, слоты объекта находящегося в NewThread запускать свои методы, слоты именно в своём потоке? Или так и придётся созавать цеочку: QML: onClicked - сигнал Mediator, далее на стороне C++:сигнал Mediator-слот MyObj с Qt::ConnectionType::QueuedConnection? А moveToThread по факту переносит только поля объекта в новый поток, а вся логика объекта так и будет проворачиваться в потоке, где объект был инициализирован?

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

5
Evgenii Legotckoi
  • 24 августа 2020 г. 2:34

Проблема в том, что если объекту, который наследован от QObject не устанавливать parent, то QML обычно забирает права владения на этот объект, а это может ещё привести и к удалению сборщиком мусора. С другой стороны, сама по себе архитектура как Qt так и QML предполагает, что графические объекты обязаны обрабатываться в главном GUI потоке. То есть правильным подходом является перенос бизнес логики в отдельный потоки, но никак не GUI объектов.
То есть работать то может в каких-то случаях и будет, но не факт, что стабильно.

    A
    • 24 августа 2020 г. 2:51

    Значит всё, что я регистрирую для QML через rootContext() или import, забирается QML (и не важно, что я переношу это в другой поток до этого). Но как тогда правильно выстроить процесс взаимодействия GUI (этот же onClicked) с MyObj, который должен полностью выполнять свою логику в другом потоке (объект отвечает за работу с аппаратурой). Как я описал, делаю через объект Mediator, который как раз и остаётся в GUI потоке. Думаю, мне нужно Ваше пояснение поподробнее о "правильным подходом является перенос бизнес логики в отдельный поток", как корректнее это сделать, чтобы я мог взаимодействовать из GUI с объектом, чья логика должна выполняться в отдельном потоке.

      Evgenii Legotckoi
      • 24 августа 2020 г. 3:03
      • Ответ был помечен как решение.

      Значит всё, что я регистрирую для QML через rootContext() или import, забирается QML (и не важно, что я переношу это в другой поток до этого)

      Полагаю, что да. Вот более подробное объяснение такой проблемы QML - Урок 023. Охота за багами при передаче указателя на QObject в QML . Там, конечно, не используется установка контекста, но уверен, что работать будет также, как и в статье описано.

      Я пока не до конца понял как именно вы используете медиатор, но полагаю, что наши мысли сходны, просто называем это по разному.

      Я бы сделал так, создаю объект, который через setContext переносится в QML, обычно у меня в таком случае всегда есть какой-то объект, класс которого называется AppCore , и он отвечает за бизнес логику. В данном классе есть методы, слоты, сигналы и т.д. которые вызываются в QML, через них же передаются в данный класс данные для обработки. И вот уже внутри AppCore есть уже другие классы, объекты которых я передаю в другие потоки через moveToThread . И уже эти классы будут обрабатывать всю бизнес логику внутри других потоков. А там уже AppCore будет управлять установкой значений и получений данных для обработки из QML.

      Полагаю, что я описал сейчас тот же самый ваш медиатор, но только с той разницей, что я не буду передавать объекты QML в C++. Я буду формировать данные, которые вводятся в объекты QML и уже их буду передавать в AppCore для дальнеёшей обработки в отдельных потоках.

      С QML есть ещё другие подводные камни в том плане, что там свой конвейер для отрисовки, и может быть дополнительное распарллеливание данных. Лично я не настолько много копался в исходниках QML, чтобы добровольно раскладывать вокруг себя грабли, поэтому и не стал бы пытаться дёргать QML объекты в другие потоки.

        A
        • 24 августа 2020 г. 4:21

        Да, я поступаю также. AppCore (мой Meditator) пораждает все необходиме нам объекты, распределяет их по потокам, и вся обработка происходит в методах (слотах) этих объектов в своих потоках. Но выходит приходится создавать цепочки connect между AppCore и обрабатывающими объектами, с указанем Qt::ConnectionType::QueuedConnection или Qt::ConnectionType::AutoConnection иначе данные и логика могут оказаться в разных потоках. Вот и выходит длинная цепочка, которую я думал можно сокраить: из QML вызывать сигналы/слоты AppCore которые в свою очередь будут соединяться(или вызывать) слоты (обработчики) объектов-обработчиков, что живут уже в других потоках. P.S. Была крамольная идея убрать все эти цепочки, зарегистрировав внутренний для AppCore объект-обработчк (MyObj) через Q_PROPERTY и геттер, но в итоге лёгкая конструкция onCliced: AppCore.MyObj.slot_do_some() , как теперь ясно почему, запускалась в GUI потоке а не в потоке MyObj. Спасибо за ответ, значит я двигался в верном направлнении. Последний вопрос, для уточнения, часто встречал этот термин "бизнес логика" когда искал решение своего вопроса и работы с MVC (в нашем случае MVD, так как у нас Delegate) Зачем вводится новый термин для процесса обработки?

          Evgenii Legotckoi
          • 24 августа 2020 г. 4:34

          Скорее это просто обобщённый термин для всего того, что скрыто от пользователя. В Случае MVC/MV - бизнес логика сидит в модели. В случае с медиатором, она сидит за медиатором (AppCore), который уже делегирует обработку данных в объекты, которые крутятся в других потоках. В случае с Web бизнес логика будет сидеть на бэкенде (особенно заметно, когда за бэкенд и фронтенд отвечают разные сервера).

            Комментарии

            Только авторизованные пользователи могут публиковать комментарии.
            Пожалуйста, авторизуйтесь или зарегистрируйтесь
            e
            • ehot
            • 31 марта 2024 г. 14:29

            C++ - Тест 003. Условия и циклы

            • Результат:78баллов,
            • Очки рейтинга2
            B

            C++ - Тест 002. Константы

            • Результат:16баллов,
            • Очки рейтинга-10
            B

            C++ - Тест 001. Первая программа и типы данных

            • Результат:46баллов,
            • Очки рейтинга-6
            Последние комментарии
            k
            kmssr8 февраля 2024 г. 18:43
            Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
            АК
            Анатолий Кононенко5 февраля 2024 г. 1:50
            Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
            EVA
            EVA25 декабря 2023 г. 10:30
            Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
            J
            JonnyJo25 декабря 2023 г. 8:38
            Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
            G
            Gvozdik18 декабря 2023 г. 21:01
            Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
            Сейчас обсуждают на форуме
            a
            a_vlasov14 апреля 2024 г. 6:41
            Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
            Павел Дорофеев
            Павел Дорофеев14 апреля 2024 г. 2:35
            QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
            f
            fastrex4 апреля 2024 г. 4:47
            Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
            P
            Pisych27 февраля 2023 г. 4:04
            Как получить в массив значения из связанной модели? Спасибо, разобрался:))
            AC
            Alexandru Codreanu19 января 2024 г. 11:57
            QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

            Следите за нами в социальных сетях