A
Artisan22 августа 2020 г. 23: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 г. 12:34

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

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

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

      Evgenii Legotckoi
      • 24 августа 2020 г. 13: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 г. 14: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 г. 14:34

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

            Комментарии

            Только авторизованные пользователи могут публиковать комментарии.
            Пожалуйста, авторизуйтесь или зарегистрируйтесь
            Ua

            Qt - Тест 001. Сигналы и слоты

            • Результат:84баллов,
            • Очки рейтинга4
            Ua

            Qt - Тест 001. Сигналы и слоты

            • Результат:42баллов,
            • Очки рейтинга-8
            ОК

            Qt - Тест 001. Сигналы и слоты

            • Результат:47баллов,
            • Очки рейтинга-6
            Последние комментарии
            ИМ
            Игорь Максимов22 ноября 2024 г. 21:51
            Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
            Evgenii Legotckoi
            Evgenii Legotckoi31 октября 2024 г. 23:37
            Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
            A
            ALO1ZE19 октября 2024 г. 17:19
            Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
            ИМ
            Игорь Максимов5 октября 2024 г. 16:51
            Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
            d
            dblas55 июля 2024 г. 20:02
            QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
            Сейчас обсуждают на форуме
            f
            firstlunoxod15 февраля 2025 г. 13:46
            Рисование на QGraphicsScene при зажатой кнопке мыши Подскажите, пожалуйста! Как данный класс можно дополнить, чтобы созданные объекты можно было перемещать мышкой по сцене?
            Дмитрий
            Дмитрий3 февраля 2025 г. 16:24
            Создание deb-пакета. Как создать ярлык на рабочем столе после установки собственного deb-пакета? Всем привет. Сделал свой deb-пакет с программой. Всё устанавливается и работает. Ставлю по пути /usr/bin/my_application. Как для пользователя при установке пакета сразу создать ярлык на раб…
            NW
            Nayo Wai30 января 2025 г. 19:22
            не запускается компьютер!!! Не запускается компьютер (точнее работает блок , но сам монитор вообще жесть)В общем я ничего с интернета не скачивала в последнее время. На компе никаких левых пр…
            n
            nkly3 января 2025 г. 12:52
            Нужно запретить перемещение только некоторых итемов, остальные перемещать можно. Вопрос решен. Узнать QModelIndex элемента на который мы перетаскиваем другой элемент, можно с помощью функции indexAt(event->position().toPoint()) представления QTreeViev вызываемой в переопр…
            M
            Marsel17 августа 2023 г. 0:26
            OAuth2.0 через VK, получение email Спасибо большое за помощь и простите за то что отнял время своей невнимательностью.

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