Russian Qt Forum
Ноябрь 23, 2024, 08:41 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 5   Вниз
  Печать  
Автор Тема: MoveToThread (все-таки хочется понять ..)  (Прочитано 47905 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Март 20, 2012, 13:01 »

Добрый день

Просматривал статейку http://habrahabr.ru/post/115835/.

Цитировать
Решение, которое очень часто предлагают на форумах и блогах – добавить в конструктор Thread команду moveToThread(this):

        class Thread : public QThread {
            Q_OBJECT
        public:
            Thread() {
                moveToThread(this); // WRONG
            }
        
            /* ... */
        };

которое действительно поможет (потому что принадлежность объекта Thread изменится), но это очень плохое решение. Что же плохого тут? Да то, что мы недопонимаем назначения объекта потока (потомков QThread): объекты QThread – это не потоки; они всего лишь объекты для управления потоками, которые используются в другом потоке (обычно, в котором они находятся).

Аргументация чисто "на понтах" - мол, "недопонимаем" но вот чего?  Улыбающийся Мы хотим чтобы сигналы (посланные Thread) принимались в eventLoop этой нитки, moveToThread это обеспечит. Далее в этой статье говорится что нужно мувать объекты в нитку. Ясно это тоже будет работать, но чем же плохо переместить (изменить принадлежность) объекта Thread ?

Эта тема часто мелькает тут и там, Авварон объяснял но я лично не понял (туповат). Попробуем начать "от печки" - чем так (wrong выше) плохо? Где/как это может заклинить? На живом простом примере, а не сваливаться в обезьянничанье (типа "а вот там напысано!!!"). В конце-концов не все что пишут - правда  Улыбающийся

Спасибо
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #1 : Март 20, 2012, 13:23 »

Добрый день

Просматривал статейку http://habrahabr.ru/post/115835/.

Цитировать
Решение, которое очень часто предлагают на форумах и блогах – добавить в конструктор Thread команду moveToThread(this):

        class Thread : public QThread {
            Q_OBJECT
        public:
            Thread() {
                moveToThread(this); // WRONG
            }
        
            /* ... */
        };

которое действительно поможет (потому что принадлежность объекта Thread изменится), но это очень плохое решение. Что же плохого тут? Да то, что мы недопонимаем назначения объекта потока (потомков QThread): объекты QThread – это не потоки; они всего лишь объекты для управления потоками, которые используются в другом потоке (обычно, в котором они находятся).

Аргументация чисто "на понтах" - мол, "недопонимаем" но вот чего?  Улыбающийся Мы хотим чтобы сигналы (посланные Thread) принимались в eventLoop этой нитки, moveToThread это обеспечит. Далее в этой статье говорится что нужно мувать объекты в нитку. Ясно это тоже будет работать, но чем же плохо переместить (изменить принадлежность) объекта Thread ?

Эта тема часто мелькает тут и там, Авварон объяснял но я лично не понял (туповат). Попробуем начать "от печки" - чем так (wrong выше) плохо? Где/как это может заклинить? На живом простом примере, а не сваливаться в обезьянничанье (типа "а вот там напысано!!!"). В конце-концов не все что пишут - правда  Улыбающийся

Спасибо
Ну тут 2 вопроса - одно действительно люди недопонимают (не мы с вами) когда нужно, а когда не нужно использовать мув ту тред от самого треда. Есть задачи, в которых это будет проще написать (чем делать пару тред + объект-воркер) - когда надо к примеру перед вызовом exec() в треде сделать немного инити - для этого надо сабклассить тред. И раз уж мы отсаклассили его, то и слоты проще написать в нем, чем создавать еще и класс-воркер со слотами.
А второй вопрос "чистоты" архитектуры - тред задуман как КОНТЕЙНЕР для потока, место, где разрываются связи парент/чайлд и начинается полностью новый конктекст - ведь QObject не может иметь парентом объект в другом треде. И тред получается "мостом" между двумя иерархиями объектов - в родительском потоке и в дочернем. При этом сам тред принадежит родительскому потоку - тк именно он "должен" знать когда стартовать/стопать и удалять тред. И получается не очень логично - мы создаем и удаляем тред из родительского потока, а метод thread() с этим потоком не совпадает. Как-то так, извиняюсь за сумбурность:)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Март 20, 2012, 13:27 »

На живом простом примере написано тут http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
И эту статью я читал, думаю и др тоже. С архитектурными соображениями (мол, здесь логичнее агрегат вместо наследования) вполне согласен. Но техническая ошибка-то где?

Если конкретно - класс QThread не спроектирован как потокобезопасный, поэтому вызов его методов (start, terminate, wait, etc) из основного потока, когда сам объект живет сам в себе, может закончиться плохо. А может и не закончиться - как повезет
Каким образом? Если я вызываю start/wait из 2 и более ниток?  Ну так я всегда получу по ушам, moveToThread здесь ни при чем

Но это только ИМХО автора статьи, ибо проблемы многопоточности очень ускользающи и придумать пример, когда это 100% приведет к проблемам, практически невозможно
Засомневались - уже хорошо  Улыбающийся
Записан
Bepec
Гость
« Ответ #3 : Март 20, 2012, 13:28 »

Просили же не воды, а камня!!!

Почитал статейку...

Цитировать
People show their code, or examples based on their code, and I often end up thinking:

You’re doing it wrong
Уже весело. (далее замечу, что автор в конце уточняет, что это нифига не правильное решение, нифига не рекомендуемое решение, а просто его мнение...)

А уж дальнейшие рассуждения...
Конечно же он не был спроектирован как класс для moveToThread.

Но он имеет все возможности для этого, как и любой унаследованный от QObject. Не лучше, не хуже. Хоть создай свой с 0, хоть moveToThread(this) сделай.

Мы имеем возможность(документированную).
Мы хотим этого(агась, так точно).
К чему плохому это приведёт? Только к рабочему коду!(то, что я унаследуюсь от Q***** любого объекта не испортит многопоточности, не? Или каждый QWidget будет в гробу вертеться?)

И наконец уверяют - создайте другой класс И... сделайте moveToThread()...

Смысл? Будет то же самое, вид сбоку, правда будет ещё головная боль с родителями.
 - (тот же QNetworkAcessManager, если его создать отдельным классом в главной нитке, не даст себя переместить в дополнительную)


Уверяют - этот же класс можно будет упрятать и в другой поток, и вообще это гуд... А мне НУЖЕН класс, работающий в собственной нитке и не с кем не делящей её.

PS а так вся шебурха по этому поводу - бред помоему.

upd: Конечно есть шанс, что это приведёт в дальнейшем, в 0,000001% случаев к краху нитки и порче программе, НО. До этого сотни тысяч раз крах и порча случится по моей(да и любого) вине. Изза архитектуры/переменных/чужого кода/мутексов. Даже чужая программа может спокойно повредить любые данные внутри программы с тем же шансом, если не больше. Таки что же делать - тройное копирование, контроль доступа к памяти и вуаля - собственная операционная система?

upd to Авварон:
Я вот незнаю какой выигрыш от одного класса в потоке. И вы что-то об этом не пишите Подмигивающий Таки откуда ж я узнаю(если не учитывать телепатию и вмешательство инопланетян)Непонимающий

Вопрос чистоты - ты используешь контейнеры для хранения контейнеров? Класс для хранения классов? Контейнер для хранения структур для хранения классов?
Таки он и остаётся контейнером.

Ты не удаляешь его, указатель на него остаётся, связь то не теряется.

PS и ради интереса - зачем так сказать в программе может пригодится thread нити и знание её родителя?
Записан
mutineer
Гость
« Ответ #4 : Март 20, 2012, 13:33 »

Писать в тред было ошибкой с моей стороны - удалил. Бессмысленный флейм это. Мне не сложно неиспользовать moveToThread(this) ибо от неиспользования этой конструкции вреда точно не будет. Видимо вам всем сложно от нее отказаться (иначе не было бы вопроса) - ваше право
Записан
Странник
Гость
« Ответ #5 : Март 20, 2012, 13:37 »

зачем объект QThread помещать в поток, которым он же управляет - непонятно. пользы не вижу, проблем достаточно. как минимум придется обеспечить потокобезопасность и учесть, что цикл обработки событий потока будет запущен после создания объекта QThread и завершится до его уничтожения.
Записан
V1KT0P
Гость
« Ответ #6 : Март 20, 2012, 13:47 »

зачем объект QThread помещать в поток, которым он же управляет - непонятно. пользы не вижу, проблем достаточно. как минимум придется обеспечить потокобезопасность и учесть, что цикл обработки событий потока будет запущен после создания объекта QThread и завершится до его уничтожения.
Если ты про moveToThread, то это нужно чтоб все сигналы выполнялись в его контексте. Я сам на такое напоролся, и сервак за неделю два раза из-за этого падал. Если бы я сразу вместо наследования от QThread, просто переносил обработчик событий объектов в него этого бы не случилось. И то я про это узнал чисто случайно.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #7 : Март 20, 2012, 13:52 »


Мы имеем возможность(документированную).
Мы хотим этого(агась, так точно).
К чему плохому это приведёт? Только к рабочему коду!(то, что я унаследуюсь от Q***** любого объекта не испортит многопоточности, не? Или каждый QWidget будет в гробу вертеться?)

И наконец уверяют - создайте другой класс И... сделайте moveToThread()...

Документация по треду безнадежно устарела, тк была написана в то время, когда у треда run() был чисто виртуальный и НЕ вызывал exec.
Статья лишь говорит о том, что сменилась парадигма использования тредов (что не отражено в документации _ну воообще никак_) - вместо чтоб сабклассить тред делать воркеры, тк это "более кутешно"- вы можете делать несколько воркеров в 1м треде, вы можете делать несколько тредов с воркерами и с сигнал-слотами это вам ничего не стоит. Побуду кэпом - если у вас встанет задача переиспользования ниток, то с сабклассингом вы ничего сделать не сможете - вам придется удалять класс треда и писать воркеров. Не проще сразу написать "как правильно"?
А с КуНАМом это косяк кутешников, но непонятно напуркуа его создавать в треде, есди он и так создает мильен тредов внутри себя и весь асинхронный? Нет, ну конечно парсинг приходящих данных в глав потоке будет тормозить, но зачем так делать? Передавайте прочтенную дату в тред на обработку...
Записан
Bepec
Гость
« Ответ #8 : Март 20, 2012, 13:56 »

Странник, ты же вумный - поясни в чём "плохость данного метода". И да, можно конкретики аля "делаем вот так, падает допустим через 4 часа".

to В1ктор - ты уверен, что именно из-за ээтого падал, а не из-за качества твоего кода и понимания того, что ты делаешь? Могу ошибаться , заранее прощения прошу, но не в той ли ты теме отписался аля "переписал по новому без moveToThread - заработало"? Я тож когда программу переписываю у меня всё работает Оо...  Особенно когда вместо неправильного, правильное пишу Подмигивающий

PS А если есть примерчик нестабильной работы из-за moveToThread(), то выложи плз.?


update to Авварон - Я собственно и спрашиваю - мне нужен поток-воркер(адын, который будет создаваться n раз). Зачем мне писать воркер + нить.

И таки да - Вопрос то стоит, чем это плохо? Где может заклинить? (Амбэ ситуации аля "придётся потом архитектуру перекраивать" опустите пожалуйста).
« Последнее редактирование: Март 20, 2012, 13:57 от Bepec » Записан
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #9 : Март 20, 2012, 14:00 »

Проблемы будут как минимум с удалением потока (причём в релизной и дебажной версии работа может отличаться).
Наследники QObject'а не любят, когда их удаляют не из их потока.

То есть у нас варианты:

1) сделать delete this; в конце run()
При этом надо думать о том, что бы по указателю не обратиться к уже самоудалённому потоку и т.п.

2) сделать moveToThread( X ); в конце run отправив поток умирать другой поток (заведом живой на этом цикле работы программы). Если программа отключается то это так же может быть проблематичным.
Записан
V1KT0P
Гость
« Ответ #10 : Март 20, 2012, 14:03 »

to В1ктор - ты уверен, что именно из-за ээтого падал, а не из-за качества твоего кода и понимания того, что ты делаешь? Могу ошибаться , заранее прощения прошу, но не в той ли ты теме отписался аля "переписал по новому без moveToThread - заработало"? Я тож когда программу переписываю у меня всё работает Оо...  Особенно когда вместо неправильного, правильное пишу Подмигивающий
Точно, сервер сам по себе простой, но там потоки общаются с общими данными, так вот из-за того что я наследовался от треда и не мувил его сам в себя обработчик слотов запускался не оттуда откуда я ожидал. У меня все было построено так что к данным в потоке напрямую из другого потока нельзя изменить. Оказалось что я неправильно сделал и некоторые функции выполнялись не из того потока, что иногда приводит к порче данных и падению сервера. Добавил везде мувы сами в себя и уже вторую неделю стабильно работает. Короче щас пойду поем и попробую сделать пример.
Записан
Bepec
Гость
« Ответ #11 : Март 20, 2012, 14:05 »

Я могу ошибаться конечно, но разве при правильном завершении секции run() поток не умирает?

Во всяком случае в "живых" его впоследствии не наблюдается(дебагер вроде врать не должен, не?).

PS при завершении работы выдрать паузу на завершение потоков можно вполне. А от reset() спасёт только молитва Ктулху.


to V1ктор, помоему вы только что сказали, что проблема была в вашей архитектуре, не? Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Март 20, 2012, 14:08 »

Давайте попробуем как-то "организоваться"  Улыбающийся
1) Итак, простецкий пример
Цитировать
class Thread : public QThread {
            Q_OBJECT
        public:
            Thread() {
                moveToThread(this); // dusk but not wrong ;-)
            }
        
            /* ... */
        };
технически ничем не плох и ни к каким крашам не приведет. Так ли это? (мое мнение что так)

2) Др дело это может быть неудачным архитектурным решением - это тоже интересно обсудить, но сначала закончим с 1 а то будем прыгать туда-сюда

Спасибо
Записан
BRE
Гость
« Ответ #13 : Март 20, 2012, 14:12 »

технически ничем не плох и ни к каким крашам не приведет. Так ли это? (мое мнение что так)
Ничем не плох. Все будет работать. Улыбающийся

Как говаривал профессор Преображенский: "Никогда не читайте перед обедом советских газет." Подмигивающий

С точки зрения архитектуры, IMHO, лучше относится к QThread как в "управляльщику" потока, а не как к потоку.
« Последнее редактирование: Март 20, 2012, 14:14 от BRE » Записан
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #14 : Март 20, 2012, 14:14 »

Igors, BRE а с удалением то что и как? Подмигивающий
Записан
Страниц: [1] 2 3 ... 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.106 секунд. Запросов: 23.