A
Aug. 22, 2020, 11:37 p.m.

И вновь о потоках и 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 по факту переносит только поля объекта в новый поток, а вся логика объекта так и будет проворачиваться в потоке, где объект был инициализирован?

2

Do you like it? Share on social networks!

5
Evgenii Legotckoi
  • Aug. 24, 2020, 12:34 p.m.

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

    A
    • Aug. 24, 2020, 12:51 p.m.

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

      Evgenii Legotckoi
      • Aug. 24, 2020, 1:03 p.m.
      • The answer was marked as a solution.

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

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

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

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

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

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

        A
        • Aug. 24, 2020, 2:21 p.m.

        Да, я поступаю также. 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
          • Aug. 24, 2020, 2:34 p.m.

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

            Comments

            Only authorized users can post comments.
            Please, Log in or Sign up
            • Last comments
            • AK
              April 1, 2025, 11:41 a.m.
              Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
            • Evgenii Legotckoi
              March 9, 2025, 9:02 p.m.
              К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
            • VP
              March 9, 2025, 4:14 p.m.
              Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
            • ИМ
              Nov. 22, 2024, 9:51 p.m.
              Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
            • Evgenii Legotckoi
              Oct. 31, 2024, 11:37 p.m.
              Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup