385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
|
||||||||||||||||||||||||||||||||||||
1 | ||||||||||||||||||||||||||||||||||||
Два способа использования QThread19.05.2012, 23:26. Показов 39564. Ответов 13
Метки нет (Все метки)
Тема скорее для обсуждения и для кого-то информационная.
Для примера возьмем традиционный пример - создать два класса (TextClass), выдающих свой номер в стандартный вывод. Вывод распараллелить по времени. Класс, печатающий в поток: TestClass.h
Стандартный метод наследования от QThread, переопределения метода run() и запуска. Описан в документации. Создаем специализированные потоки: SpecialThread.h
main.cpp
Используем QThread "как задумывалось создателем". С версии 4.4 QThread не является абстрактным классом и его можно инстанцировать. Класс стал именно интерфейсом для доступа к управлению потоком, но не представлением самого потока, как это сделано в подходе 1. Механизм строится целиком на сигналах-слотах. Сигнал запуска потока запускает и вывод на консоль. Сигнал окончания вывода останавливает поток. Код:
Не относится к QThread, но стоит указать способ с QtConcurrent::run() как наиболее простой и "продвинутый":
1-ый явно не соответствует идеи "не плодить сущностей". Для каждого конкретного случая понадобится создавать отдельного наследника QThread. Создание универсального параматрезированного варианта (способного принять любой объект и хотя бы запустить конкретный его метод в своей реализации run()) может потребовать больших усилий и погружения на мета-объектный уровень классов. Однако этот способ дает наиболее полный контроль над выполнением потока. В простейшем случае (один класс потока - один класс обрабатываемых классов) даже не потребуется наследование от QObject обрабатываемых классов. Кроме того, довольно легко реализуется передача аргументов в метод, который будет крутить в потоке. 2-ой подход избавляет от написания лишнего кода и все стройно укладывается в идеологию сигналов-слотов. Передать в новый поток можно любой объект и любой его метод просто перенастроив соединения. Однако в случае, если в метод run() потребуется передать аргументы, то все сильно усложняется. Т.о. необходимо "настраивать" классы перед использованием и создавать отдельный слот для простого запуска "обработчика". Кроме того, накладывается строгое ограничение, что класс, передаваемый в поток должен быть вовлечен в "сигналы-слоты". Для чистых C++-шных классов это потребует написания обертки, либо рефакторинга кода. Кроме того, все 2 подхода страдают сложностью с возвращаемым значением, которое можно будет получить только через сигнал. 3-ий подход не страдает ни одним из перечисленных выше недостатков, но цена тому - отсутствие какого-либо контроля. Интересны ваши мнения.
9
|
19.05.2012, 23:26 | |
Ответы с готовыми решениями:
13
Два способа решения - два разных ответа два способа, один не работает Два способа создания методов Два способа решения задачи |
22.05.2012, 09:38 | 2 |
че то все смешалось в кучу... ИМХО правильным и масштабируемым решением будет некий гибрид 1-го и 2-го способов, т.е. делаем свой объект потока с нужными нам свойствами, и который может принимать в качестве аргумента другой наш объект (и не только), а всю связь межну ними и остальной логикой сделать на сигналах/слотах
0
|
385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
|
|
22.05.2012, 19:04 [ТС] | 3 |
ну, это и есть первый способ. просто связь с другой частью программы я тут не показывал - она одинакова в обоих случаях.
по поводу масштабируемости можно поспорить. если завтра понадобится обрабатывать в отдельных потоках совершенно новые данные, не связанные с прошлыми, то это выльется в создание нового сабкласса от QThread. а это не есть хорошо. и хорошо, если подобное изменение впишется в принятую архитектуру. во втором же методе потоки ничего не знают о данных, которые им предстоит обработать и они "всеядны". Если потребуется, то используя второй метод легко создать, скажем, единый пул потоков, который предоставит интерфейс для включения многопоточности в любой точке программы для любых классов без какого-либо дополнительного кода. т.е. реализовать способ №3 руками.
0
|
22.05.2012, 23:59 | 4 |
Зачем? Отдельной нитью работает не сам поток, а то что в него засунуто (в run())
Т.е. сабкласс от QThread у нас один, но передаем туда каждый раз разные классы, иерархию правда придется сделать с виртуальным методом, который и будет в потоке выполняться. Код
CMyBaseClass { virtual void ThreadMetod() = 0; }: CMyClass1 public CMyBaseClass { void ThreadMetod() {//} }: CMyClass2 public CMyBaseClass { void ThreadMetod() {//} }: Как то так можно. Ну и естетсвенно, что надо будет продумать интерфейс связи сигналов/слотов между потоком и базовым классом. Добавлено через 1 минуту PS: ну и QObject::moveToThread() никто не отменял
0
|
385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
|
|
23.05.2012, 01:00 [ТС] | 5 |
можно, конечно, передавать в QThread имя вызываемого в потоке метода, список аргументов, потом через мета-объектную информацию этот метод вызывать в run(). Но огород получается внушительный. Стоит ли это того? Если все тоже самое выполняется черех move ToThread
0
|
oxotnik
|
23.05.2012, 08:53
#6
|
0
|
9 / 9 / 0
Регистрация: 28.02.2011
Сообщений: 45
|
|||||||||||||||||||||
18.10.2012, 03:46 | 7 | ||||||||||||||||||||
А возможно ли в объекте такого унаследованного класа оперировать объектом, например типа QTextBrowser? Далее более подробно, что я хочу:
0
|
18.10.2012, 08:54 | 8 |
Нельзя гуйней напрямую руководить из дочернего (не гуевого) потока. Посылай сигналы из потока с нужными данными, а в основном гуевом потоке эти сигналы принимай и обрабатывай.
2
|
18.10.2012, 23:36 | 9 |
Ничего создавать не надо, есть QThreadPool
Толсто, смотри на обобщенные функторы Александреску, Gof паттерн Комманда(Command), а в C++11 есть чудесные лямбда выражения. По поводу moveToThread, то есть классы, экземпляры которых могут использоваться только в том потоке, в котором он создан.
0
|
51 / 149 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
|
|
02.10.2020, 08:22 | 10 |
почему-то нет в v5.12 (а может у меня build такой)?...
хотя видела Добавлено через 15 минут нашла пример здесь... проходит такой #include <QtConcurrent/QtConcurrent>
0
|
13 / 13 / 1
Регистрация: 19.10.2019
Сообщений: 607
|
|
22.03.2021, 16:28 | 11 |
Вот читаю эту тему девятилетней давности и статью https://habr.com/ru/post/150274/
и не могу понять: что хорошего в способе 2, если класс TestClass необходимо оставлять без родителя и он не будет иметь тех гарантий очистки памяти, как и остальные.
0
|
683 / 458 / 160
Регистрация: 01.10.2015
Сообщений: 1,264
|
||||||
22.03.2021, 17:00 | 12 | |||||
Есть статья Threads Events QObjects в Qt-вики, найдите раздел Feature comparison, он состоит из элементарной таблички, наглядно демонстрирующей разницу между подходами. Следует понимать, что это разные инструменты, и как любой инструмент, они имеют свои ограничения и области применимости.
0
|
13 / 13 / 1
Регистрация: 19.10.2019
Сообщений: 607
|
|
22.03.2021, 17:38 | 13 |
0x90h,
В табличке сравниваются не эти способы. Подозреваю, что Вы меня не поняли. При использовании movetothread мы должны передавать объекты у которых нет родителя (свойство parent не установлено) В первом же случае, нам не нужно использовать moveToThread, поэтому TestClass может установить себе родителя, хотя автор этим пренебрег. Добавлено через 25 минут Петля событий при первом варианте может быть, а при втором скорей всего тоже(хотя я не пробовал ни разу). Третий способ, я не рассматриваю.
0
|
Алексей1153
|
22.03.2021, 19:42
Два способа использования QThread
#14
|
Не по теме: squareroot, с моей точки зрения самый удобный (а для меня - и самый правильный) - это способ №1. Только у ТС косяк - run должен быть "бесконечным циклом" + обработка петли сообщений ( QThread::eventDispatcher() или exec() )
0
|
22.03.2021, 19:42 | |
Два способа считывать данные от пользователя Написать еще два способа решения Два способа работы с базой данных ? Зачем существует два способа обращения к объектам? Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |