И вновь о потоках и moveToThread.
Для создания потоков я успешно использую подход 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 по факту переносит только поля объекта в новый поток, а вся логика объекта так и будет проворачиваться в потоке, где объект был инициализирован?
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.Do you like it? Share on social networks!
- Akiv Doros
- Nov. 11, 2024, 10:58 p.m.
C ++ - Test 004. Pointers, Arrays and Loops
- Result:50points,
- Rating points-4
- molni99
- Oct. 26, 2024, 8:37 a.m.
C ++ - Test 004. Pointers, Arrays and Loops
- Result:80points,
- Rating points4
- molni99
- Oct. 26, 2024, 8:29 a.m.
C ++ - Test 004. Pointers, Arrays and Loops
- Result:20points,
- Rating points-10
Проблема в том, что если объекту, который наследован от QObject не устанавливать parent, то QML обычно забирает права владения на этот объект, а это может ещё привести и к удалению сборщиком мусора. С другой стороны, сама по себе архитектура как Qt так и QML предполагает, что графические объекты обязаны обрабатываться в главном GUI потоке. То есть правильным подходом является перенос бизнес логики в отдельный потоки, но никак не GUI объектов.
То есть работать то может в каких-то случаях и будет, но не факт, что стабильно.
Значит всё, что я регистрирую для QML через rootContext() или import, забирается QML (и не важно, что я переношу это в другой поток до этого). Но как тогда правильно выстроить процесс взаимодействия GUI (этот же onClicked) с MyObj, который должен полностью выполнять свою логику в другом потоке (объект отвечает за работу с аппаратурой). Как я описал, делаю через объект Mediator, который как раз и остаётся в GUI потоке. Думаю, мне нужно Ваше пояснение поподробнее о "правильным подходом является перенос бизнес логики в отдельный поток", как корректнее это сделать, чтобы я мог взаимодействовать из GUI с объектом, чья логика должна выполняться в отдельном потоке.
Полагаю, что да. Вот более подробное объяснение такой проблемы QML - Урок 023. Охота за багами при передаче указателя на QObject в QML . Там, конечно, не используется установка контекста, но уверен, что работать будет также, как и в статье описано.
Я пока не до конца понял как именно вы используете медиатор, но полагаю, что наши мысли сходны, просто называем это по разному.
Я бы сделал так, создаю объект, который через setContext переносится в QML, обычно у меня в таком случае всегда есть какой-то объект, класс которого называется AppCore , и он отвечает за бизнес логику. В данном классе есть методы, слоты, сигналы и т.д. которые вызываются в QML, через них же передаются в данный класс данные для обработки. И вот уже внутри AppCore есть уже другие классы, объекты которых я передаю в другие потоки через moveToThread . И уже эти классы будут обрабатывать всю бизнес логику внутри других потоков. А там уже AppCore будет управлять установкой значений и получений данных для обработки из QML.
Полагаю, что я описал сейчас тот же самый ваш медиатор, но только с той разницей, что я не буду передавать объекты QML в C++. Я буду формировать данные, которые вводятся в объекты QML и уже их буду передавать в AppCore для дальнеёшей обработки в отдельных потоках.
С QML есть ещё другие подводные камни в том плане, что там свой конвейер для отрисовки, и может быть дополнительное распарллеливание данных. Лично я не настолько много копался в исходниках QML, чтобы добровольно раскладывать вокруг себя грабли, поэтому и не стал бы пытаться дёргать QML объекты в другие потоки.
Да, я поступаю также. 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) Зачем вводится новый термин для процесса обработки?
Скорее это просто обобщённый термин для всего того, что скрыто от пользователя. В Случае MVC/MV - бизнес логика сидит в модели. В случае с медиатором, она сидит за медиатором (AppCore), который уже делегирует обработку данных в объекты, которые крутятся в других потоках. В случае с Web бизнес логика будет сидеть на бэкенде (особенно заметно, когда за бэкенд и фронтенд отвечают разные сервера).