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

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #30 : Февраль 25, 2010, 11:17 »

Это, в принципе, уже было предложено Igors. см. мой ответ http://www.prog.org.ru/index.php?topic=12552.msg79842#msg79842
Но это так, чисто академически. А вообще, основная засада тут в том, что рабочие потоки - асинхронны, т.е. принимают не задачи в виде данных, которые можно обработать и забыть, а в виде QObject-ов, которые крутятся в их цикле событий. Причем нагрузка от этих объектов принципиально непредсказуема - у одного может быть единственный обработчик сообщений, а у другого - полста сигналов и слотов на QueuedConnection. Поэтому поток не может принимать решение о том, что пора взять еще один объект на обработку, основываясь на количестве обслуживаемых объектов. Вот я и пытаюсь выделить критерий, чтобы понять, насколько загружен Event Loop потока.
Давайте разберемся. Выходит у Вас задачи привязаны к ниткам? Напр. клиент начинает что-то посылать. Ладно, послал первый пакет, она из ниток сервера его приняла. Теперь серия след. пакетов (от этого клиента) будет приходить на ту же нитку сервера. Но поскольку неизвестно когда клиент продолжит посылку - Вы занимаете нитку чем-то еще. Я верно понял?
Записан
ритт
Гость
« Ответ #31 : Февраль 25, 2010, 11:26 »

Цитировать
итак, идея следующая: запросы (в вашей терминологии "задачи"?) складываются в простую очередь (QQueue) в порядке их получения (полагаю, это довольно честно, если учесть, что обычно клиенты не стараются заддосить сервер, а это - отдельная тема), а каждый рабочий поток сигнализирует о завершении обработки вверенного ему запроса - кто раньше отметился, что свободен, тот и получает на обработку следующий запрос из очереди (первый, если учесть, что у нас схема FIFO), если таковой имеется. при получении запроса от клиента создаём рабочие потоки по принципу стандартного пула (нужен новый и общее кол-во меньше максимального? - создаём; иначе выходим и первый освободившийся поток в дальнейшем получит данный запрос на обработку).
мысли/комментарии?
Это, в принципе, уже было предложено Igors. см. мой ответ http://www.prog.org.ru/index.php?topic=12552.msg79842#msg79842
вовсе нет. на пост Igors'а я и сам отвечал выше. принципиальная разница в том, что нет необходимости синхронизировать потоки при обращении к очереди задач (другими словами, не нужно защищать очередь задач мутексом), что снимает вопрос о негативном влиянии на производительность. при непустой очереди задач поток будет простаивать (между окончанием выполнения задачи и началом выполнения новой) лишь константное время, необходимое на выборку из QQueue. как характеризуется задача - набором байт или проинициализированным наследником QObject'а - в данном случае значения не играет абсолютно.
предыдущий комментарий был бы справедлив лишь в случае, когда задачи в очереди (объекты) взаимодействуют друг с другом между потоками (1) или в том же потоке (2), но  это накладывало бы непреодолимые ограничения на минимальное кол-во потоков (1) или на максимальную нагрузку на поток (2) и по большому счёту тогда и "взвешивать" затраты на работу с объектами не имеет смысла. методом исключения отбрасываю этот случай.
« Последнее редактирование: Февраль 25, 2010, 11:28 от Константин » Записан
kramer3d
Гость
« Ответ #32 : Февраль 25, 2010, 11:45 »

предыдущий комментарий был бы справедлив лишь в случае, когда задачи в очереди (объекты) взаимодействуют друг с другом между потоками (1) или в том же потоке (2), но  это накладывало бы непреодолимые ограничения на минимальное кол-во потоков (1) или на максимальную нагрузку на поток (2) и по большому счёту тогда и "взвешивать" затраты на работу с объектами не имеет смысла. методом исключения отбрасываю этот случай.
Не совсем понял. Естественно, объекты, принятые потоками на обслуживание взаимодействуют друг с другом, в том числе и с объектами из соседних потоков (как правило, через отправку событий, но бывают и QueuedConnections). Иначе б зачем нужно было использовать здесь QObject и вообще о какой асинхронности можно говорить? Если говорить об объектах в очереди, ну, допустим, пока объект стоит в очереди, он не взаимодействует с остальными (неважно, как это может быть реализовано), но будучи принятым потоком на обслуживание встает в even-loop этого потока, который обрабатывает его события. Как я уже говорил, непонимание между нами именно в том, что поток не обрабатывает объекты и выкидывает их потом, а они в нем живут. А поток занимается обработкой их событий. Которых может и не быть. А может быть очень много. И поток, получается, неспособен оценить свою загрузку штатными методами, и принять решение о взятии нового объекта на обслуживание. И приходится изобретать костыли вроде таймеров для измерения латентности цикла событий в потоке.
Записан
ритт
Гость
« Ответ #33 : Февраль 25, 2010, 11:53 »

в таком случае, я недопонимаю саму задачу...
скажем, пришёл запрос на получение содержимого такого-то файла - создали QFile (или чем там файл будет читаться?) и делегировали потоку; следующий запрос - скажем, удаление того же файла - создали объект и делегировали потоку. о чём эти объекты между собой общаются и почему они не будут удалены после отправки соответствующих ответов клиенту(ам)?
Записан
kramer3d
Гость
« Ответ #34 : Февраль 25, 2010, 12:09 »

Знаете, что? Бог с ними, с задачами, серверами и прочая-прочая. Давайте остановимся на многопоточности и асинхронности исполнения.
И переформулируем вопрос следующим образом:

1. Есть некоторое количество объектов-наследников QObject. Эти объекты могут быть инстансами разных классов, но у всех у них есть сигналы, слоты и обработчики событий.
2. Обработчики событий и слоты могут требовать некоторых вычислительных ресурсов.
3. Объектов может быть очень много, поэтому обработку их событий и соединений сигнал/слот нужно сделать многопоточной.
4. Обработчики событий и слоты могут опосредованно постить события объектам, находящимся как в собственном потоке, так и в других потоках.
5. Потоки не занимаются ничем, кроме обслуживания объектов, т.е. могут быть фактически инстансами QThread.
5. Во время выполнения объекты создаются и уничтожаются динамически, исходя из каких-либо внешних событий (ну, хоть взаимодействия с пользователем)
6. При создании нового объекта необходимо назначить ему обслуживающий поток (основной поток у нас занимается только отслеживанием внешних событий и созданием новых объектов)

Вопрос: каким образом при назначении обслуживающего потока выяснить его загрузку? Под загрузкой понимаем в данном случае латентность его цикла обработки событий, и на основании сравнения латентности с некоторым эмпирически полученным значением делаем вывод, стоит ли добавлять данный объект в исследуемый поток, или его event-loop и так предельно загружен и события объекта будут дольше стоять в очереди, чем исполняться, т.е. необходимо создать новый поток?
Записан
kramer3d
Гость
« Ответ #35 : Февраль 25, 2010, 12:18 »

в таком случае, я недопонимаю саму задачу...
скажем, пришёл запрос на получение содержимого такого-то файла - создали QFile (или чем там файл будет читаться?) и делегировали потоку; следующий запрос - скажем, удаление того же файла - создали объект и делегировали потоку. о чём эти объекты между собой общаются и почему они не будут удалены после отправки соответствующих ответов клиенту(ам)?
В целом - так оно и есть. Просто получение данных от клиентов и их некоторую часть их обслуживания, которая не делегируется бэкэнду, я тоже хочу сделать многопоточными. Т.е. в данном случае объекты - это наследники QObject-а, содержащие сокеты и еще кой-какую функциональность, и представляют собой абстракцию типа "клиентское подключение". Так вот обслуживание этих клиентских подключений я хочу сделать однородно-многопоточным, и для того-то мне и нужно определять загруженность цикла событий потока.
Записан
ритт
Гость
« Ответ #36 : Февраль 25, 2010, 13:29 »

мне кажется, это перебор...
в любом случае, всё ещё не понимаю о чём эти объекты-клиентские подключения между собой общаются
Записан
BRE
Гость
« Ответ #37 : Февраль 25, 2010, 13:32 »

Все таки я не вижу смысла в таком распределении ролей.  Подмигивающий
Есть разные задачи, для каждого вида задач есть специальный класс, все они являются наследниками класса Job.
Как они устроенны внутри пока забываем - черный ящик. Объект такого класса получает входные параметры, выполняет саму работу и при завершении сообщает нам, что такая-то задача завершилось и можно забрать результат.
Пользователю нужно посчитать контрольную сумму файла, он создал объект класса CalcFileChecksumJob, в конструкторе передав имя файла и положил в пул задач. Пул поручает эту работу первой простаивающей нитке.
В дальнейшем, появился новый тип задач, дописали соответствующий класс. Никаких дополнительных телодвижений для его интеграции в систему делать не нужно.

Теперь, по поводу реализации задач. Некоторым задачам вообще не нужен цикл обработки событий (открыл файл, пробежался по нему, закрыл файл, завершился), для чего его запускать. Для других нужен - создали необходимые объекты для выполнения (QTcpSocket, QTimer, ...), запустили цикл, задача выполнилась - все остановили, завершились.

Т.е. реализация каждого задания спрятана в своем классе, нужно что-то поменять/изменить - пожалуйста, основная система ничего и знать не будет.


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

Сообщений: 11445


Просмотр профиля
« Ответ #38 : Февраль 25, 2010, 13:52 »

и положил в пул задач. Пул поручает эту работу первой простаивающей нитке.
Ну может не первой а той что больше всего простояла. Но что пул нужен - это точно. Нехорошо помещать задачу в очередь нитки если она занята. На эмпирические оценки надежда слабая - хотя бы потому что нагрузка может измениться на ходу.
Записан
BRE
Гость
« Ответ #39 : Февраль 25, 2010, 13:58 »

Ну может не первой а той что больше всего простояла.
А в чем разница, между свободной ниткой которая долго простояла и той, которая только что освободилась?
Первая лучше отдохнула?  Улыбающийся
Записан
kramer3d
Гость
« Ответ #40 : Февраль 25, 2010, 14:03 »

мне кажется, это перебор...
в любом случае, всё ещё не понимаю о чём эти объекты-клиентские подключения между собой общаются
В принципе, в контексте вопроса это не так важно. Они могут и не общаться между собой вовсе, но все равно обязаны обрабатывать события, полученные от остальных участников системы. Которыми, в частности, могут быть и другие клиентские подключения. Ну, например, скажем, пришли объекту-клиенту данные, он их десериализовал, вызвал локальный обработчик, который выяснил, что эти данные нафик никому не нужны, и предназначаются к немедленной отправке другому клиенту. Обработчик дернет из списка ссылку на объект другого клиентского подключения, и, если тот существует (бишь, клиент подключен), то, непосредственно, минуя бэкэнд, поставит этому клиенту эти данные в очередь на отправку (запостив ему соответствующее событие с ссылкой на эти данные). Другое дело, если клиента в онлайне нет, и тогда обработчик встанет в очередь на обращение к бэкэнду (там шедулер предусмотрен, ибо бэкэндов может быть несколько), тот поставит эти данные в очередь deferred data, и займется другими делами. Диспетчер подключений при подключении нового клиента опрашивает бэкэнд на предмет наличия для клиента данных очереди deferred data, и если таковые имеются, отдаст их на отправку. Можно не морочится, и сделать все по второй схеме, но это во-первых дополнительно загрузит бэкэнд, а во-вторых, по прежнему остается та же проблема с обработкой сообщений от бэкэнда. Ведь бэкэнд живет в другом потоке, и общаться с клиентским подключением (читай, вызывать его методы) напрямую не имеет права, только через события.

Возможно, это действительно перебор, но и вне контекста клиент-сервер задача (вот эта: http://www.prog.org.ru/index.php?topic=12552.msg79885#msg79885 ) определенно имеет право на жизнь, и штатной возможности определения латентности цикла событий явно не хватает. Понятно, для GUI это не нужно, там всего один поток, но ведь Qt это уже давно больше чем GUI. Улыбающийся
Записан
kramer3d
Гость
« Ответ #41 : Февраль 25, 2010, 14:10 »

Все таки я не вижу смысла в таком распределении ролей.  Подмигивающий
Есть разные задачи, для каждого вида задач есть специальный класс, все они являются наследниками класса Job.
Как они устроенны внутри пока забываем - черный ящик. Объект такого класса получает входные параметры, выполняет саму работу и при завершении сообщает нам, что такая-то задача завершилось и можно забрать результат.
Пользователю нужно посчитать контрольную сумму файла, он создал объект класса CalcFileChecksumJob, в конструкторе передав имя файла и положил в пул задач. Пул поручает эту работу первой простаивающей нитке.
В дальнейшем, появился новый тип задач, дописали соответствующий класс. Никаких дополнительных телодвижений для его интеграции в систему делать не нужно.
Теперь, по поводу реализации задач. Некоторым задачам вообще не нужен цикл обработки событий (открыл файл, пробежался по нему, закрыл файл, завершился), для чего его запускать. Для других нужен - создали необходимые объекты для выполнения (QTcpSocket, QTimer, ...), запустили цикл, задача выполнилась - все остановили, завершились.
Т.е. реализация каждого задания спрятана в своем классе, нужно что-то поменять/изменить - пожалуйста, основная система ничего и знать не будет.
Повторюсь - речь идет не о задачах и распределении их по потокам, а об объектах, эти задачи генерирующих. Эти объекты управляются внешними событиями, например, данными из сокета, и формируют из этих данных задачи и отправляют бэкэнду в очередь. Эти объекты живут в нескольких потоках, и обязаны обрабатывать сообщения от бэк-энда и друг от друга. Просто изначально я избрал немного неверную терминологию, и теперь мучаюсь, пытаясь объяснить, чего же я все-таки хочу.
Записан
BRE
Гость
« Ответ #42 : Февраль 25, 2010, 14:32 »

Давай будем смотреть на объекты.  Улыбающийся
Есть объект-поток ConnectionManager, в нем находятся все объекты-соединения с каждым клиентов Connection. В этом потоке запущен eventloop.
У одного из соединений срабатывает сигнал readyRead, в контексте потока ConnectionManager обрабатывается слот, который читает данные из нужного сокета и определяет, что команда пришла полностью. В этот момент испускает сигнал newCommand( Command ).  Все ConnecionManager отправл команду и забыл...
В объекте класса Command содержится вся информация о команде. Кто, где и в каком потоке будет ловить этот сигнал решать тебе.
Предположим он ловиться в объекта-потоке CommandManager. Он в свою очередь решает какой объект нужно создать для решения поступившей команды, создает его и кладет в пул задач. Все, CommandManager забыл о задачи, до тех пор пока она не будет завершена.
Как только результат сформирован, CommandManager посылает сигнал readyResult, его обрабатывает ConnectionManager и ставит в очередь на отправку данные для клиента.

Т.е. все разделено, CommandManager не может на пряму отсылать данные через сокет, он может попросить об этом ConnectionManager с помошью сигналов.
Если сразу продумать интерфейсы и протоколы такого взаимодействия, то разные части системы не будут зависить друг от друга.

Или мы опять про разные вещи говорим?  Улыбающийся
Записан
kramer3d
Гость
« Ответ #43 : Февраль 25, 2010, 14:58 »

Давай будем смотреть на объекты.  Улыбающийся
Есть объект-поток ConnectionManager, в нем находятся все объекты-соединения с каждым клиентов Connection. В этом потоке запущен eventloop.
[skipped]
Т.е. все разделено, CommandManager не может на пряму отсылать данные через сокет, он может попросить об этом ConnectionManager с помошью сигналов.
Если сразу продумать интерфейсы и протоколы такого взаимодействия, то разные части системы не будут зависить друг от друга.
Или мы опять про разные вещи говорим?  Улыбающийся
Уже горячо. Улыбающийся Единственная разница - я хочу сам ConnectionManager сделать многопоточным, т.е. он сам не поток, а имеет в своем распоряжении несколько потоков, в которых живут объекты-соединения CClientConnection. Вообще, сам класс ConnectionManager (у меня называется CConnectionDispatcher) хранит QMap<CUserId, CClientConnection*>, где CUserId = login+peeraddress, обновляет его при коннектах-дисконнектах и предоставляет кому угодно ссылки на объекты-подключения. При этом у объектов-подключений нет ни единого публичного метода, кроме CUserId getUserId(), т.е. инкапсуляция не нарушается, а все коммуникации осуществляются через события. При этом CClientConnection сам принимает данные от своих сокетов, десериализует их, вызывает для них обработчик, который уже решает, что с ними делать - то ли проэмитить на всю систему сигнал incomingData(data*), а там глядишь кто-нибудь поймает, то ли разобраться с этими данными самостоятельно (например, если эти данные - текстовое сообщение другому клиенту, или, того хуже клиент спрашивает, сколько у него открытых тасков на сервере, а это уже локальные данные и для самого объекта CClientConnection). Т.е. весь вопрос в том, как держать объекты-соединения в нескольких потоках.
Записан
ритт
Гость
« Ответ #44 : Февраль 25, 2010, 15:06 »

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


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