Название: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 20, 2012, 13:01 Добрый день
Просматривал статейку http://habrahabr.ru/post/115835/ (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 выше) плохо? Где/как это может заклинить? На живом простом примере, а не сваливаться в обезьянничанье (типа "а вот там напысано!!!"). В конце-концов не все что пишут - правда :) Спасибо Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Авварон от Март 20, 2012, 13:23 Добрый день Ну тут 2 вопроса - одно действительно люди недопонимают (не мы с вами) когда нужно, а когда не нужно использовать мув ту тред от самого треда. Есть задачи, в которых это будет проще написать (чем делать пару тред + объект-воркер) - когда надо к примеру перед вызовом exec() в треде сделать немного инити - для этого надо сабклассить тред. И раз уж мы отсаклассили его, то и слоты проще написать в нем, чем создавать еще и класс-воркер со слотами.Просматривал статейку http://habrahabr.ru/post/115835/ (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 выше) плохо? Где/как это может заклинить? На живом простом примере, а не сваливаться в обезьянничанье (типа "а вот там напысано!!!"). В конце-концов не все что пишут - правда :) Спасибо А второй вопрос "чистоты" архитектуры - тред задуман как КОНТЕЙНЕР для потока, место, где разрываются связи парент/чайлд и начинается полностью новый конктекст - ведь QObject не может иметь парентом объект в другом треде. И тред получается "мостом" между двумя иерархиями объектов - в родительском потоке и в дочернем. При этом сам тред принадежит родительскому потоку - тк именно он "должен" знать когда стартовать/стопать и удалять тред. И получается не очень логично - мы создаем и удаляем тред из родительского потока, а метод thread() с этим потоком не совпадает. Как-то так, извиняюсь за сумбурность:) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 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% приведет к проблемам, практически невозможно Засомневались - уже хорошо :)Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 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 нити и знание её родителя? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: mutineer от Март 20, 2012, 13:33 Писать в тред было ошибкой с моей стороны - удалил. Бессмысленный флейм это. Мне не сложно неиспользовать moveToThread(this) ибо от неиспользования этой конструкции вреда точно не будет. Видимо вам всем сложно от нее отказаться (иначе не было бы вопроса) - ваше право
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Странник от Март 20, 2012, 13:37 зачем объект QThread помещать в поток, которым он же управляет - непонятно. пользы не вижу, проблем достаточно. как минимум придется обеспечить потокобезопасность и учесть, что цикл обработки событий потока будет запущен после создания объекта QThread и завершится до его уничтожения.
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: V1KT0P от Март 20, 2012, 13:47 зачем объект QThread помещать в поток, которым он же управляет - непонятно. пользы не вижу, проблем достаточно. как минимум придется обеспечить потокобезопасность и учесть, что цикл обработки событий потока будет запущен после создания объекта QThread и завершится до его уничтожения. Если ты про moveToThread, то это нужно чтоб все сигналы выполнялись в его контексте. Я сам на такое напоролся, и сервак за неделю два раза из-за этого падал. Если бы я сразу вместо наследования от QThread, просто переносил обработчик событий объектов в него этого бы не случилось. И то я про это узнал чисто случайно.Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Авварон от Март 20, 2012, 13:52 Мы имеем возможность(документированную). Мы хотим этого(агась, так точно). К чему плохому это приведёт? Только к рабочему коду!(то, что я унаследуюсь от Q***** любого объекта не испортит многопоточности, не? Или каждый QWidget будет в гробу вертеться?) И наконец уверяют - создайте другой класс И... сделайте moveToThread()... Документация по треду безнадежно устарела, тк была написана в то время, когда у треда run() был чисто виртуальный и НЕ вызывал exec. Статья лишь говорит о том, что сменилась парадигма использования тредов (что не отражено в документации _ну воообще никак_) - вместо чтоб сабклассить тред делать воркеры, тк это "более кутешно"- вы можете делать несколько воркеров в 1м треде, вы можете делать несколько тредов с воркерами и с сигнал-слотами это вам ничего не стоит. Побуду кэпом - если у вас встанет задача переиспользования ниток, то с сабклассингом вы ничего сделать не сможете - вам придется удалять класс треда и писать воркеров. Не проще сразу написать "как правильно"? А с КуНАМом это косяк кутешников, но непонятно напуркуа его создавать в треде, есди он и так создает мильен тредов внутри себя и весь асинхронный? Нет, ну конечно парсинг приходящих данных в глав потоке будет тормозить, но зачем так делать? Передавайте прочтенную дату в тред на обработку... Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 13:56 Странник, ты же вумный - поясни в чём "плохость данного метода". И да, можно конкретики аля "делаем вот так, падает допустим через 4 часа".
to В1ктор - ты уверен, что именно из-за ээтого падал, а не из-за качества твоего кода и понимания того, что ты делаешь? Могу ошибаться , заранее прощения прошу, но не в той ли ты теме отписался аля "переписал по новому без moveToThread - заработало"? Я тож когда программу переписываю у меня всё работает Оо... Особенно когда вместо неправильного, правильное пишу ;) PS А если есть примерчик нестабильной работы из-за moveToThread(), то выложи плз.? update to Авварон - Я собственно и спрашиваю - мне нужен поток-воркер(адын, который будет создаваться n раз). Зачем мне писать воркер + нить. И таки да - Вопрос то стоит, чем это плохо? Где может заклинить? (Амбэ ситуации аля "придётся потом архитектуру перекраивать" опустите пожалуйста). Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: LisandreL от Март 20, 2012, 14:00 Проблемы будут как минимум с удалением потока (причём в релизной и дебажной версии работа может отличаться).
Наследники QObject'а не любят, когда их удаляют не из их потока. То есть у нас варианты: 1) сделать delete this; в конце run() При этом надо думать о том, что бы по указателю не обратиться к уже самоудалённому потоку и т.п. 2) сделать moveToThread( X ); в конце run отправив поток умирать другой поток (заведом живой на этом цикле работы программы). Если программа отключается то это так же может быть проблематичным. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: V1KT0P от Март 20, 2012, 14:03 to В1ктор - ты уверен, что именно из-за ээтого падал, а не из-за качества твоего кода и понимания того, что ты делаешь? Могу ошибаться , заранее прощения прошу, но не в той ли ты теме отписался аля "переписал по новому без moveToThread - заработало"? Я тож когда программу переписываю у меня всё работает Оо... Особенно когда вместо неправильного, правильное пишу ;) Точно, сервер сам по себе простой, но там потоки общаются с общими данными, так вот из-за того что я наследовался от треда и не мувил его сам в себя обработчик слотов запускался не оттуда откуда я ожидал. У меня все было построено так что к данным в потоке напрямую из другого потока нельзя изменить. Оказалось что я неправильно сделал и некоторые функции выполнялись не из того потока, что иногда приводит к порче данных и падению сервера. Добавил везде мувы сами в себя и уже вторую неделю стабильно работает. Короче щас пойду поем и попробую сделать пример.Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 14:05 Я могу ошибаться конечно, но разве при правильном завершении секции run() поток не умирает?
Во всяком случае в "живых" его впоследствии не наблюдается(дебагер вроде врать не должен, не?). PS при завершении работы выдрать паузу на завершение потоков можно вполне. А от reset() спасёт только молитва Ктулху. to V1ктор, помоему вы только что сказали, что проблема была в вашей архитектуре, не? :) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 20, 2012, 14:08 Давайте попробуем как-то "организоваться" :)
1) Итак, простецкий пример Цитировать class Thread : public QThread { технически ничем не плох и ни к каким крашам не приведет. Так ли это? (мое мнение что так)Q_OBJECT public: Thread() { moveToThread(this); // dusk but not wrong ;-) } /* ... */ }; 2) Др дело это может быть неудачным архитектурным решением - это тоже интересно обсудить, но сначала закончим с 1 а то будем прыгать туда-сюда Спасибо Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 14:12 технически ничем не плох и ни к каким крашам не приведет. Так ли это? (мое мнение что так) Ничем не плох. Все будет работать. :)Как говаривал профессор Преображенский: "Никогда не читайте перед обедом советских газет." ;) С точки зрения архитектуры, IMHO, лучше относится к QThread как в "управляльщику" потока, а не как к потоку. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: LisandreL от Март 20, 2012, 14:14 Igors, BRE а с удалением то что и как? ;)
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 14:16 Igors, BRE а с удалением то что и как? ;) сделать delete this; в конце run() == выстрелить себе в ногу, потом отрезать в месте прострела ножом и поджечь. :) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 14:19 Таки дык животрепещущий вопрос - если поток дошёл до конца run(), он самоликвидируется, али что?
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 14:21 Таки дык животрепещущий вопрос - если поток дошёл до конца run(), он самоликвидируется, али что? Поток да, объект QThread нет.Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 20, 2012, 14:26 С точки зрения архитектуры, IMHO, лучше относится к QThread как в "управляльщику" потока, а не как к потоку. Как уже отметил Авварон есть масса случаев когда данных (с которыми работает нитка) - с гулькин нос, и городить там воркера нет желания, просто обработка в самой нитке - и все делаIgors, BRE а с удалением то что и как? ;) Не знаю, мне никогда не приходило в голову "дать нитке еще и родителя". Если он нулевой то и проблем нетНазвание: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 14:29 Интересный вопрос. У меня вон валяется программа, создающая до 18к QThread'ов последовательно. Причём создаются без родителя, в конструкторе moveToThread и не удаляются. Однако утечки там вроде нет (память не жрёться, стандартное потребление).
Или всё же утечка там присутствует? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 14:29 Как уже отметил Авварон есть масса случаев когда данных (с которыми работает нитка) - с гулькин нос, и городить там воркера нет желания, просто обработка в самой нитке - и все дела В этих случаях я никого не стесняясь делаю moveToThread( this ). :)Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: LisandreL от Март 20, 2012, 14:37 Не знаю, мне никогда не приходило в голову "дать нитке еще и родителя". Если он нулевой то и проблем нет Ну если вы ей родителя дадите из основного потока то у вас просто напросто moveToThread не сработает.Может приведёте минимальный компилируемый пример где нет проблем? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 20, 2012, 14:38 Интересный вопрос. У меня вон валяется программа, создающая до 18к QThread'ов последовательно. Причём создаются без родителя, в конструкторе moveToThread и не удаляются. Однако утечки там вроде нет (память не жрёться, стандартное потребление). Ну если Вы напр создаете через new и забыли delete - то присутствует :) А так откуда ей взяться?Или всё же утечка там присутствует? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 14:39 Можно удалять объект потока при завершении работы потока соединив сигнал finished со слотом deleteLater.
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 20, 2012, 14:45 Может приведёте минимальный компилируемый пример где нет проблем? Недавно с лабой помогал http://www.prog.org.ru/index.php?topic=21135.msg145276#msg145276 (http://www.prog.org.ru/index.php?topic=21135.msg145276#msg145276) (UI взял у V1KT0P, а нитки мои) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: mutineer от Март 20, 2012, 14:46 Можно удалять объект потока при завершении работы потока соединив сигнал finished со слотом deleteLater. deleteLater() обрабатывается в eventLoop, к которому привязан QThread. Но когда испускается finished() этот eventLoop уже завершен. Отработает ли deleteLater() ? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 14:47 deleteLater() обрабатывается в eventLoop, к которому привязан QThread. Но когда испускается finished() этот eventLoop уже завершен. Отработает ли deleteLater() ? Это если объект QThread перемещен в себя.Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: mutineer от Март 20, 2012, 14:49 deleteLater() обрабатывается в eventLoop, к которому привязан QThread. Но когда испускается finished() этот eventLoop уже завершен. Отработает ли deleteLater() ? Это если объект QThread перемещен в себя.Ну да. Разве тут не этот случай рассматривают? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: LisandreL от Март 20, 2012, 14:54 Можно удалять объект потока при завершении работы потока соединив сигнал finished со слотом deleteLater. Если связать через очередь, то удаление не произойдёт, так как цикл exec уже не существует.Если напрямую: Код
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 14:55 А что мешает просто delete NNN; ?
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: mutineer от Март 20, 2012, 15:03 Разве тут не этот случай рассматривают? Напугал старика. :)А причем здесь очередь сообщений, если здесь соединение будет DirectConnect? :) А причем тут тип коннекта? deleteLater() не удаляет же, а постит event удаления в eventLoop. А eventLoop уже закончился и до этого event'a не дойдет и объект не удалит Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 15:05 Таки дык. То есть пришли к мнению, что поток с конструктором moveToThread ничего плохого не может сделать, если не считать из пальца выдуманных ситуаций и непродуманной архитектуры?
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 15:05 А причем тут тип коннекта? deleteLater() не удаляет же, а постит event удаления в eventLoop. А eventLoop уже закончился и до этого event'a не дойдет и объект не удалит Точно-точно. Спешу. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Странник от Март 20, 2012, 15:14 Странник, ты же вумный - поясни в чём "плохость данного метода". И да, можно конкретики аля "делаем вот так, падает допустим через 4 часа". просто потому, что такое решение создает проблемы на пустом месте и менее универсально. возникает два противоречивых требования: с одной стороны, объект QThread, живущий в потоке, хорошо бы уничтожить до завершения потока, но с другой стороны, прежде чем уничтожить QThread мы должны завершить работу потока, прервав цикл обработки событий через QThread::exit(). как правильно завершить работу потока и удалить объект QThread в этом случае? и вы начинаете ломать себе мозг, чтобы не наступить на грабли, которые сами же разложили.Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 20, 2012, 15:22 Таки дык. То есть пришли к мнению, что поток с конструктором moveToThread ничего плохого не может сделать, если не считать из пальца выдуманных ситуаций и непродуманной архитектуры? Да, вроде разобрались - само по себе moveToThread в конструкторе не есть баг (а то тыкают в нос линками :)). Ладно, поехали по архитектуре?Прошу показать пример/псевдокод где наследование от QThread явно плохо/неудачно. просто потому, что такое решение создает проблемы на пустом месте и менее универсально. возникает два противоречивых требования: с одной стороны, объект QThread, живущий в потоке, хорошо бы уничтожить до завершения потока, но с другой стороны, прежде чем уничтожить QThread мы должны завершить работу потока, прервав цикл обработки событий через QThread::exit(). как правильно завершить работу потока и удалить объект QThread в этом случае? и вы начинаете ломать себе мозг, чтобы не наступить на грабли, которые сами же разложили. Нет корректного способа убить нитку (и объект QThread) если она активна (не вышла из run). Наверное Вы имели ввиду что-то другое - тогда пояснитеНазвание: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 15:24 Убивать нитки надо самому, а deleteLater() - просто более удобный инструмент, НЕПОДХОДЯЩИЙ в данной ситуации. И таких ситуаций я могу привести много ;) Он далеко не универсален.
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: V1KT0P от Март 20, 2012, 15:25 Что то не лезет ко мне в голову пример при котором наследование от QThread и использовании moveToThread будет ронять сервер.
В моем понимании наследование от QThread является сродни использованию goto. И тем и тем можно спокойно обойтись, но в будущем может привести к ненужным костылям. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 15:27 Виктор а помоему вы чуть чуть суть не улавливаете :)
Плохого в этом ничего нет. Ни явного, ни скрытого. Точно так же любой виджет наследуется от QWidget. Просто создаётся аналог(!) QWidget в другом потоке с необходимым нам набором функций. Можно разбить его на 2/4/6/8/n+2 более простых элементов, но зачем? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: mutineer от Март 20, 2012, 15:31 Да, вроде разобрались - само по себе moveToThread в конструкторе не есть баг (а то тыкают в нос линками :)). Так вопрос в использовании moveToThread в конструкторе или в использовании конструкций moveToThread(this) и thread->moveToThread(thread) ? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 15:34 Так вопрос в использовании moveToThread в конструкторе или в использовании конструкций moveToThread(this) и thread->moveToThread(thread) ? Объясните мне разницу mutineer,пожалуйста, между moveToThread(this) и thread->moveToThread(thread) А так вопрос в использовании конструкции <потомок_QThread>.moveToThread(<потомок_QThread>); Сам в себя, проще говоря. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: mutineer от Март 20, 2012, 15:36 Так вопрос в использовании moveToThread в конструкторе или в использовании конструкций moveToThread(this) и thread->moveToThread(thread) ? Объясните мне разницу mutineer,пожалуйста, между moveToThread(this) и thread->moveToThread(thread) А так вопрос в использовании конструкции <потомок_QThread>.moveToThread(<потомок_QThread>); Сам в себя, проще говоря. Разницы нет, кроме встреченного возгласа на форуме "а я не использую moveToThread(this), я делаю thread->moveToThread(thread)". Написал оба случая, чтобы не придирались подобным образом Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Авварон от Март 20, 2012, 15:56 Да, вроде разобрались - само по себе moveToThread в конструкторе не есть баг (а то тыкают в нос линками :)). Ладно, поехали по архитектуре? Прошу показать пример/псевдокод где наследование от QThread явно плохо/неудачно. Нет корректного способа убить нитку (и объект QThread) если она активна (не вышла из run). Наверное Вы имели ввиду что-то другое - тогда поясните На самом деле, с архитектурной точки зрения - сабклассить тред - это использовать его не по назначению. Если у вас есть разовая операция над небольшими (большими?) данными - используйте конкаррент. Кутред (по идее) надо использовать, если есть асинхронная схема событие-обработчик. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 20, 2012, 15:57 Я конечно боюсь вас прерывать... Но многое, если не всё, применяется совершенно неожиданно для разработчиков. Те же "ляпы" новичков, где строка является массивом, long boolем и прочая.
А конкурент... Я его ещё не изучал, ибо нужны были нитки - изучал нитки :) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Авварон от Март 20, 2012, 16:07 Я конечно боюсь вас прерывать... Но многое, если не всё, применяется совершенно неожиданно для разработчиков. Те же "ляпы" новичков, где строка является массивом, long boolем и прочая. А конкурент... Я его ещё не изучал, ибо нужны были нитки - изучал нитки :) Может быть вам был нужен конкурент как раз?:) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 20, 2012, 16:12 А второй вопрос "чистоты" архитектуры - тред задуман как КОНТЕЙНЕР для потока, место, где разрываются связи парент/чайлд и начинается полностью новый конктекст - ведь QObject не может иметь парентом объект в другом треде. И тред получается "мостом" между двумя иерархиями объектов - в родительском потоке и в дочернем. При этом сам тред принадежит родительскому потоку - тк именно он "должен" знать когда стартовать/стопать и удалять тред. И получается не очень логично - мы создаем и удаляем тред из родительского потока, а метод thread() с этим потоком не совпадает. Как-то так, извиняюсь за сумбурность:) Мне кажется QThread просто (ну или по крайней мере в первую очередь) - это удобный класс для запуска ниток. Мы можем использовать его как контейнер или нет. Да, нитка стартует из другой (обычно главной) но зачем нам категорично утверждать ее "принадлежность"? Напр естественная/популярная схема:- создал нитку и втулил moveToThread - нитка создает свои объекты (в конструкторе или др местах) и удаляет их в деструкторе. Сигналы работают как надо (через eventLoop нитки) Др словами нитка как бы "самодостаточна". Повторное использование - дождался завершения, удалил, создал снова - не проблема. Да, я "разорвал связи", да, я не говорю что это лучше чем "выносить воркера". Но чем это плохо? Где ужасные "костыли", "грабли" о которых так много говорится/пишется? :) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: BRE от Март 20, 2012, 17:48 просто потому, что такое решение создает проблемы на пустом месте и менее универсально. возникает два противоречивых требования: с одной стороны, объект QThread, живущий в потоке, хорошо бы уничтожить до завершения потока, но с другой стороны, прежде чем уничтожить QThread мы должны завершить работу потока, прервав цикл обработки событий через QThread::exit(). Ну на самом деле противоречий никаких нет. Понятие "объект QThread живет в потоке" это очень абстрактное понятие, которое было введено в Qt для организации передачи сигналов между потоками. На самом деле все объекты живут в одном адресном пространстве и прямая операция delete корректно этот объект разрушит.Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Март 21, 2012, 14:29 Однако же.. как быстро завершились наши архитектурные дебаты :)
Хорошо, попробуем подвести итоги, предлагаю такую формулировку Наследование от QThread (с эвентуальным moveToThread(this)) не является ошибкой - ни технической, ни архитектурной. Просто такое наследование совсем необязательно, более того, часто агрегация лучше/гибче, т.к. она более логична/идейна Спасибо за внимание Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Март 21, 2012, 15:20 И тебе спасибо за раскрытие данного дела ;)
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: andrew.k от Май 18, 2012, 18:37 У меня есть класс (class Scanner), который дергает блокирующие функции. Поэтому хочу, чтобы они отрабатывали в отдельном потоке, чтоб не вис гуй.
Допустим я захотел сделать его реализацию "архитектурно красиво" (без наследования). Но что-то у меня пока не получается красивости. Хочу чтобы в итоге выглядело так: Код
Сделать в одном классе не получается. Получается нужно делать два класса. Собственно Scanner и ScannerWorker (в котором будет реализация). А если делать это в одном классе, то нельзя будет создавать Scanner имеющим родителя. А с двумя классами получается нужно будет воркер выносить в ашник, чего бы не хотелось. Зачем он там нужен? Не это ли недостаток такой концепции? Или я что-то не так делаю? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: V1KT0P от Май 18, 2012, 18:49 Или я что-то не так делаю? Тут проблема в том что нельзя просто вызвать слот не соединяя его с сигналом. Меня это например бесит. В итоге класс напоминает примерно следующее:Код
Код
Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Странник от Май 18, 2012, 19:58 Тут проблема в том что нельзя просто вызвать слот не соединяя его с сигналом. Меня это например бесит. серьезно?Код: QMetaObject::invokeMethod(someClass, "work", Qt::QueuedConnection); Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: V1KT0P от Май 18, 2012, 20:10 Тут проблема в том что нельзя просто вызвать слот не соединяя его с сигналом. Меня это например бесит. серьезно?Код: QMetaObject::invokeMethod(someClass, "work", Qt::QueuedConnection); Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: DmitryM от Май 23, 2012, 10:19 Не очень понял весь сыр бор из-за MoveToThread, когда у connect есть пятый параметр.
Код: bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection ) [static] Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Май 23, 2012, 10:23 DmitryM - если не поняли, лучше спросить, а не тыкать строку из ассистента ;)
Обсуждение было закончено. PS а тема собственно - полезно, вредно или безразлично использование moveToThread. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: DmitryM от Май 23, 2012, 10:34 DmitryM - если не поняли, лучше спросить, а не тыкать строку из ассистента ;) Кок раз из-за этого пятого параметра можно не думать где находиться принимающий объект.Обсуждение было закончено. PS а тема собственно - полезно, вредно или безразлично использование moveToThread. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: mutineer от Май 23, 2012, 10:36 Кок раз из-за этого пятого параметра можно не думать где находиться принимающий объект. Вопрос не в том, где находится принимающий объект, а в том, корректно ли перемещать экземпляр QThread в поток, которым он управляет (this->moveToThread(this)) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: LisandreL от Май 23, 2012, 10:51 Кок раз из-за этого пятого параметра можно не думать где находиться принимающий объект. Думать всегда полезно.В каком потоке выполняется код - важная вещь, которая определяет нужна ли защита потоконебезопасных данных, где могут возникать дэдлоки, в конце концов какой из потоков будет фризиться на время выполнения слота (например надолго фризящийся гуёвый поток у пользователя вызывает негативные эмоции). Ну и опять же про пятый парметр, как показывает практика большинство новичков считает что: Код обеспечит выполнение doWork() в созданном потоке. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: DmitryM от Май 24, 2012, 09:49 Ну и опять же про пятый парметр, как показывает практика большинство новичков считает что: Кто, где и чем делают таких новичков?Код обеспечит выполнение doWork() в созданном потоке. У меня есть класс (class Scanner), который дергает блокирующие функции. Поэтому хочу, чтобы они отрабатывали в отдельном потоке, чтоб не вис гуй. Допустим я захотел сделать его реализацию "архитектурно красиво" (без наследования). Но что-то у меня пока не получается красивости. Хочу чтобы в итоге выглядело так: Код
Сделать в одном классе не получается. Получается нужно делать два класса. Собственно Scanner и ScannerWorker (в котором будет реализация). А если делать это в одном классе, то нельзя будет создавать Scanner имеющим родителя. А с двумя классами получается нужно будет воркер выносить в ашник, чего бы не хотелось. Зачем он там нужен? Не это ли недостаток такой концепции? Или я что-то не так делаю? Код ЧЯДНТ? Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Bepec от Май 24, 2012, 10:07 Дмитрий, вы "со свинным рылом в оружейный ряд" суётесь.
Тут была битва по другой теме, другому поводу и охватывающая более сложные конструкции, нежели ваш пример ;) Ключевое слово БЫЛА :) Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: andrew.k от Май 25, 2012, 23:15 ЧЯДНТ? Быдлосленг еще не умер?Как бы спасибо, конечно, но зачем ты это выложил? Прочитал бы тему с начала. Я спрашивал, как сделать в одном классе не переопределяя run! Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Май 26, 2012, 09:39 А если делать это в одном классе, то нельзя будет создавать Scanner имеющим родителя. Ну и чем это грозит? Scanner не удалится автоматычно при удалении родителя? Так это и нежелательно т.к. все равно нитку надо завершать. Поэтому лучше напр сделать указатель Scanner членом и в деструкторе разобратьсяНазвание: Re: MoveToThread (все-таки хочется понять ..) Отправлено: andrew.k от Май 27, 2012, 00:22 А если делать это в одном классе, то нельзя будет создавать Scanner имеющим родителя. Ну и чем это грозит? Scanner не удалится автоматычно при удалении родителя? Так это и нежелательно т.к. все равно нитку надо завершать. Поэтому лучше напр сделать указатель Scanner членом и в деструкторе разобратьсяНазвание: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Май 27, 2012, 10:37 Какие проблемы завершить нитку в деструкторе Scanner? Сложно что ли? Технически никаких, но это "архитектурно некрасиво" :) Чтобы завершить нитку в деструкторе Scaner, указатель на QThread должен быть членом Scanner, и зависеть от него. Тогда чего было притворяться и избегать наследования от QThread ?Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: andrew.k от Май 27, 2012, 12:16 Какие проблемы завершить нитку в деструкторе Scanner? Сложно что ли? Технически никаких, но это "архитектурно некрасиво" :) Чтобы завершить нитку в деструкторе Scaner, указатель на QThread должен быть членом Scanner, и зависеть от него. А внешний юзер даже не знал ничего про потоки. членом Scanner, и зависеть от него. Тогда чего было притворяться и избегать наследования от QThread ? Чтобы получить "архитектурно красиво".Это в этом топике еще как-то лояльно обсуждают подход. Оригинальная же статья не выделяет преимуществ и недостатков, а говорит о том, что наследоваться неправильно. QThread уже готовый класс и нечего от него наследовать. Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: Igors от Май 27, 2012, 12:57 Естественно QThread член класса. Я ж написал, хочу все в одном классе, чтобы все само работало. Как я понимаю, смысл того что пишут (и активно повторяют) в том что Scanner не должен ничего знать о том в какой QThread он выполняется. Завязывая его с QThread (через наследование или членом класса) мы теряем гибкость. Напр др экземпляр Scanner потребовался в главной нитке или хотим иметь что-то еще (кроме Scanner) в той же нитке, или двигать его туда-сюда. Это достигается легко, напрА внешний юзер даже не знал ничего про потоки. Код Да, так 2 класса, но это не накладно. Можно все в одном, ну тогда наследоваться или делать один членом другого (особой разницы нет). Название: Re: MoveToThread (все-таки хочется понять ..) Отправлено: andrew.k от Май 27, 2012, 14:44 Естественно QThread член класса. Я ж написал, хочу все в одном классе, чтобы все само работало. Как я понимаю, смысл того что пишут (и активно повторяют) в том что Scanner не должен ничего знать о том в какой QThread он выполняется. Завязывая его с QThread (через наследование или членом класса) мы теряем гибкость. Напр др экземпляр Scanner потребовался в главной нитке или хотим иметь что-то еще (кроме Scanner) в той же нитке, или двигать его туда-сюда. Это достигается легко, напрА внешний юзер даже не знал ничего про потоки. Код
Да, так 2 класса, но это не накладно. Можно все в одном, ну тогда наследоваться или делать один членом другого (особой разницы нет). в том и дело, что разница есть. Если делать членом, то экземпляр класса не должен иметь родителя.Хотелось бы чтобы он был. Но в общем-то это практически не важно. Поэтому пока сделал без родителя. Естественно QThread член класса. Я ж написал, хочу все в одном классе, чтобы все само работало. Как я понимаю, смысл того что пишут (и активно повторяют) в том что Scanner не должен ничего знать о том в какой QThread он выполняется. А внешний юзер даже не знал ничего про потоки. При желании его так же можно подвинуть в любой другой поток. |