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

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

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: Как организовать многопоточность? Выбор модели  (Прочитано 21648 раз)
deMax
Хакер
*****
Offline Offline

Сообщений: 600



Просмотр профиля
« Ответ #15 : Октябрь 24, 2016, 09:54 »

Выходит что на самом деле используется 2 потока а не 50 в итоге...
Да хоть сто создайте, зачем только если вычислений с гулькин нос и все потоки простаивают, т.к. считать им нечего. У вас же не сотни тысяч клиентов. Как правило если тяжелые вычисления количество потоков =количеству ядер + по отдельному потоку на гуи/бд/файловую систему и прочие тормозные вещи.
Записан
Bepec
Гость
« Ответ #16 : Октябрь 24, 2016, 10:01 »

Потоки эт как холопы.
Можно хоть тыщу нагнать, но если надо 2 кирпича с место на место переносить, то двое прекрасно справятся Веселый
Вот когда двое не будут справляться с нагрузкой - тогда надо добавлять.

Зависимость скорости работы от количества потоков непропорциональная и разнится от задачи к задаче. Но сейчас модно пихать многопоточность всюду, начиная от калькуляторов, заканчивая играми.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #17 : Октябрь 24, 2016, 13:31 »

Выскажите ваше мнение... по поводу того, что в интернете очень часто можно встретить программы, в которых можно указать кол. потоков для работы.
Например вот
Ну это скорее "анти-пример"  Улыбающийся

Я так понимаю что ОС не запускает на самом деле 50 потоков, а создает псевдо потоки.... (видимо они стоят в очереди на выполнение)
Нет, хоть с 1 ядром ОС честно запускает все 50 (или сколько сказали), каждому выделяется квант времени, гарантируется что нитка получит управление не позже чем <конкретное время>

Почему именно этот метод получил такое широкое распространение в народе...
Просто потому что дятлов всегда больше чем умных Улыбающийся Оно ж ведь кажется очень удобным - эта нитка занимается одной задачей, другая другой.  
Записан
rudireg
Гость
« Ответ #18 : Октябрь 24, 2016, 17:48 »

1) Правильно ли я понял тот факт, что запуская объект obj (наследник от QRunnable) на выполнение через QThreadPool,
то есть выполнив команду: QThreadPool::globalInstance()->start(obj);

То метод run() объекта obj выполняется в отдельной нитке и при этом не имеет цикла обработки событий, иными словами obj завершает свою работу как только void run() выполнил последнюю команду.

2) А если нужно что бы нитки не завершали свою работу а просматривали очередь заданий, и если там есть новые задания то брали их на выполнение, а если нет заданий то  нитка засыпает на короткое время (sleep на 1 секунду) и после снова проверяет наличие новых заданий.

Цикл ожидания (сон, и проверка новых заданий) следует реализовать своими силами тогда... верно?
Например зациклить выполнение метода run(), и опрашивать раз в секунду есть ли новое задание на выполнение... если нету, то QThread::sleep(1); и снова проверить очередь.

ВОПРОС:
В правильном ли направлении я мыслю? что бы не вызывать каждый раз QThreadPool::globalInstance()->tryStart(obj)



« Последнее редактирование: Октябрь 24, 2016, 18:03 от rudireg » Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #19 : Октябрь 24, 2016, 18:10 »

1) Правильно ли я понял тот факт.
Да

2) А если нужно что бы нитки не завершали свою работу а просматривали очередь заданий, и если там есть новые задания то брали их на выполнение, а если нет заданий то  нитка засыпает на короткое время (sleep на 1 секунду) и после снова проверяет наличие новых заданий.
Не нужно так делать - это плохое решение. Для организации таких очередей нужно использовать условные переменные (QWaitCondition).

Цикл ожидания (сон, и проверка новых заданий) следует реализовать своими силами тогда... верно?
Нет, это все уже реализовано в QThreadPool, попробуйте просто добавлять задачи через start, они ставятся в очередь и будут выполняться по мере освобождения воркеров.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #20 : Октябрь 25, 2016, 07:03 »

То метод run() объекта obj выполняется в отдельной нитке и при этом не имеет цикла обработки событий,
Да

иными словами obj завершает свою работу как только void run() выполнил последнюю команду.
Неясно что obj должен "завершать" Непонимающий

2) А если нужно что бы нитки не завершали свою работу а просматривали очередь заданий, и если там есть новые задания то брали их на выполнение, а если нет заданий то  нитка засыпает на короткое время (sleep на 1 секунду) и после снова проверяет наличие новых заданий.
Рано или поздно все приходят к одному: sleep хорош для отладки, но НИКОГДА в рабочем коде

Цикл ожидания (сон, и проверка новых заданий) следует реализовать своими силами тогда... верно?
Например зациклить выполнение метода run(), и опрашивать раз в секунду есть ли новое задание на выполнение... если нету, то QThread::sleep(1); и снова проверить очередь.
Это не запрещено, но зачем брать на себя низкоуровневую работу? Гораздо удобнее сидеть в QThread::exec и делать задание при получении сигнала

ВОПРОС:
В правильном ли направлении я мыслю? что бы не вызывать каждый раз QThreadPool::globalInstance()->tryStart(obj)
Не знаю. Ну а что такого уж плохого в QThreadPool и tryStart чтобы его избегать? Подбрасываете ему дровишки-задачки, оформить run несложно. Голова не болит какая нитка чем занимается. Минусы - пожалуй один, нельзя послать obj сигнал, он не в событийном цикле. 
Записан
rudireg
Гость
« Ответ #21 : Октябрь 25, 2016, 15:09 »

Спасибо вам Old и Igors
Понемногу начинаю разбираться... делаю тесты и выяснил следующее
Методы запуска start и tryStart класса QThreadPool имеют различия:

QThreadPool::start(); - Запускает объект унаследованный от QRunnable на выполнение.
                                          Если нет свободных зарезервированных ниток, то выполнение ставиться в очередь заданий.

QThreadPool::tryStart(); - Пытается запустить объект унаследованный от QRunnable на выполнение.
                                         Если нет свободных зарезервированных ниток, то выполнение НЕ ставиться в очередь заданий, и запуск игнорируется.

И в свою очередь метод QThreadPool::globalInstance()->waitForDone(); - Вызывает ожидание выполнения всех ниток,
                                а после удаляет объекты унаследованные от QRunnable , при условии что объект имеет статус setAutoDelete(true)
« Последнее редактирование: Октябрь 25, 2016, 15:12 от rudireg » Записан
popper
Гость
« Ответ #22 : Октябрь 26, 2016, 18:00 »

Достаточно подробно про потоки в Qt написано здесь:
Mark Summerfield
Advanced Qt Programming
Записан
rudireg
Гость
« Ответ #23 : Октябрь 27, 2016, 17:49 »

Возник вопрос.
Допустим задача программы это: отсылка сетевых запросов, получение ответа и их дальнейшая обработка (сохранение coockie, парсинг нужной информации... сохранение информации).
Сетевых запросов много... при этом используются прокси, прокси могут быть медленными... то есть ожидание ответа от сервера может ожидаться например 30 секунд и более (если прокси медленный).

Если программа многопоточная, то поток получив задание сделать сетевой запрос делает следующее:
1) Посылает запрос на сервер
2) Ожидает ответ сервера
3) Получает ответ сервера и обрабатывает его с последующим сохранением нужных данных

И если например таких запросов нужно отправить 1000 штук, а в микропроцессоре компьютера всего 4 ядра,
то получается что в один момент времени программа может работать с максимум 4-мя потоками.
Как можно оптимизировать механизм отправки и получения сетевых запросов? ведь поток ожидая ответа (если прокси медленный).... простаивает.

Ранее я уже не один раз работал в программах с сетевыми запросами, и был написан класс по отправке и приему данных
видимо пришло время его переписать, так как поток будет ожидать ответа от сервера и простаивать...

На всякий случай выкладываю исходники класса по работе с HTTP запросами
rhttp.h
http://codepad.org/VszY9dsj

rhttp.cpp
http://codepad.org/c7fH60ui

На мой вгляд, следует отправлять запрос серверу... и на этом поток завершил свою задачу...
QNetworkReply *reply = this->manager->get(request);


Далее как то нужно сохранить данные об отправке, и на сигнале finished о том что ответ пришел, снова в потоке (в любом) уже принимать ответ от сервера.
QObject::connect(reply, SIGNAL(finished()), какойто объект, SLOT(Слот Обработки ответа сервера()));

Вопрос тока, кто будет этот сигнал посылать, если поток уже отработал и уничтожился, если тока как вариант сохранять reply  в поток MANAGER (главный поток)
« Последнее редактирование: Октябрь 27, 2016, 18:14 от rudireg » Записан
Bepec
Гость
« Ответ #24 : Октябрь 27, 2016, 18:13 »

Вы НИЧЕРТА не понимаете что такое ядро, что такое поток и что такое ОС Веселый

Прошу вас - не придумывайте свои понятия.
Ядро - по сути процессор, выполняющий команды.
ОС - программа, позволяющая пользователю работать с компьютером.
Поток - это абстрактный список команд, имеющий право на часть процессорного времени.

Ядро - одно.
Ос - одна.
Потоков - сколько душе угодно. Правда есть предел потоков для ОС, но он чрезвычайно труднодостижим.
Количество потоков ни как не зависит от количества ядер.
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #25 : Октябрь 27, 2016, 18:36 »

На мой вгляд, следует отправлять запрос серверу...
Не мудрите зря. Улыбающийся
Пусть будет поток, который отправляет запросы и обрабатывает сигнал:
void QNetworkAccessManager::finished(QNetworkReply *reply)

Заметьте, это сигнал у самого менеждера и в параметре он передает указатель на reply.
Как только, ответ получен - он отправляется на обработку одному из воркеров пула потоков.
Одного потока-менеджера хватит и отправлять запросы (тысячами) и получать ответы, а обработкой будет заниматься потоки из пула.
Записан
rudireg
Гость
« Ответ #26 : Октябрь 27, 2016, 19:20 »

На мой вгляд, следует отправлять запрос серверу...
Не мудрите зря. Улыбающийся
Пусть будет поток, который отправляет запросы и обрабатывает сигнал:
void QNetworkAccessManager::finished(QNetworkReply *reply)

Заметьте, это сигнал у самого менеждера и в параметре он передает указатель на reply.
Как только, ответ получен - он отправляется на обработку одному из воркеров пула потоков.
Одного потока-менеджера хватит и отправлять запросы (тысячами) и получать ответы, а обработкой будет заниматься потоки из пула.

Получается такая сехама (поправте меня если я не прав):
1) Есть пул потоков вокеров (QThreadPool)
  (В этих нитках формируются сетевые запросы, например GET запрос http://mail.ru)

2) Есть 1 поток-менеджер, который отвечает за работу с посылкой и приемом сетевых запросов.
    (Имено в нем идет работа с QNetworkAccessManager)

Если я правильно понял схему, то потоки-вокеры  кидают сетевые запросы потоку-менеджеру (кидают через очередь наверное)
   
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #27 : Октябрь 27, 2016, 19:32 »

Поток-менеджер сам отправляет запросы и сам получает ответы, а вот обработка ответов может выполняться в несколько потоков.
Нет смысла параллелить отправку запросов, все равно все сведется к сетевому стеку ядра, который последовательно их отправит в сеть. И ждать ответа в нескольких потоках тоже нет смысла, с этим справиться и один поток, а вот когда ответ пришел и его нужно долго обрабатывать, то пригодятся отдельные потоки.
Записан
rudireg
Гость
« Ответ #28 : Октябрь 27, 2016, 20:39 »

Нарисовал схему
Верно ли я понял?

Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #29 : Октябрь 27, 2016, 20:52 »

В одном потоке живет объект QNetworkAccessManagerа, через него отправляются 1000 запросов, дальше он начинает ждать ответы... Вот пришел первый ответ - отдали его на обработку воркеру, пришел второй ответ - еще одному воркеру, и т.д.
Нет нужды отправлять запросы и пулить ответы в нескольких потоках, ждать легко может только один поток - дело не хитрое. Улыбающийся
Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


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