Вообще не понимаю о чем речь =) , програмисты пишут игры , там никакой экономии реесурсов , просто на обум так как нравится. За счет усиления технической базы игры работают как и прежде )))))) разберите код любой игры и гляньте там реализацию. Ужаснетесь , а тут млин речь о чате на сокетах =) , который нужен для практики реализации а не о совершенстве =) поэтому того что есть вполне хватит.
rei3er Да ладно. Для однозадачной ОС с поддержкой многопроцессорности, наверное, но под Windows получается очень неплохой рост производительности при количестве потоков много большем числа CPU за счет отбирания процессорного времени у других процессов. Кроме того при блокирующих сокетах как раз и будет "выполнено другое действие или операция I/O на другом сокете" во время блокирования потока, обслуживающего первый сокет, до возникновения на первом сокете очередного сетевого события. А заблокированному потоку просто не будет выделяться процессорное время. Т.о. задача распределения внимания между сокетами ИМХО очень удачно будет переложена на планировщик потоков ОС. Можно подробнее, почему блокирующие сокеты дают меньшую производительность при не слишком большом числе потоков... скажем, не более двадцати (при бОльшем увеличении числа потоков будет, возможно, излишне потеснён приоритет системных процессов, что приведет к замедлению работы системы в общем, поэтому неограниченное увеличение числа потоков очевидно не слишком благоприятствует высокой производительности)?
ну да, если система обслуживает один сервер, тогда конечно чем больше потоков, тем меньше остается процессорного времени на выполнение полезной работы (планирование и переключение контекста) и эта зависимость усиливается с ростом количества потоков кроме того, бОльшее количество потоков требует бОльшего числа ресурсов системы (в т. ч памяти для стека, внутренних структур данных ядра и т. д) а эти ресурсы могли были быть направлены на реализацию полезной работы (в т. ч установление новых соединений) вместо обслуживание инфраструктуры распараллеливания простаивающий поток - это крайне расточительно кроме того, будет затрачиваться определенное количество времени на его wake up при генерации события I/O (к примеру, получение пакета) преимущество неблокирующих сокетов хорошо видны на примере TCP connect() для чего ждать завершения рукопожатия, если в этот момент можно получать или передавать данные на другом сокете или инициировать новые соединения читай выше
rei3er Ну как будто разработчику чата есть дело до того, что будет претеснён IE. Это как раз то, о чем я писал: неограниченный рост числа потоков не в плюс, но разумный предел можно подобрать (как я уже говорил: потоков двадцать, например), который ИМХО не так катастрофично ограничивается, как Вы написали "чатом для двух человек". Наибольший объем памяти при создании потока выделяется обычно AFAIK как раз под стэк, но совсем не обязательно делать стэк мегабайтовым. Можно ограничиться килобайтами четырьмя и использовать стэк немного осторожнее. А соответствующие структуры ядра AFAIK всего ничего занимают. А вот здесь у меня сомнения насчет того, что собственная реализация распределения внимания сокетам будет хуже реализации планировщика потоков. В своей реализации на сетевое событие тоже не удастся отреагировать мгновенно. Так а connect точно так же блокирует работу потока до окончания "рукопожатия", так что, пока поток заблокирован, вполне можно инициировать новые соединения в других потоках. Так и не понял, почему. Процессорного времени не занимает, объем необходимой для него памяти можно ограничить до вполне разумных размеров. А а ля "wake up" в собственной реализации тоже будет неизбежным, и совсем не обязательно, что он будет короче wake up'а потока. В общем пока веских замечаний в пользу неблокирующих сокетов в чате на десять человек не вижу.
на 10 да я тут не спорю цифры можно? я ориентирусь на Linux, поэтому исхожу из того, как реализовано там: один дескриптор процесса (=потока) занимает пол страницы мгновенно не получится практически никогда при любом подходе однако через неблокирующие сокеты можно реализовать свою стратегию планирования действий упрощенный пример есть MP система с 4-мя CPU, на каждом работает один поток есть 10000 соединений, по 2500 на каждый поток каждый поток находится в бесконечном цикле выполняя неблокирующий select()/poll()/epoll_wait() на своих 2500 дескрипторах представим ситуацию, когда на всех 2500 дескрипторах какого-то потока произошло событие, а на остальных нет в этом случае оставшиеся три потока можно нагрузить обработкой событий на дескрипторах первого потока в случае блокируемого I/O они тупо будут простаивать, да еще и ресурсы кушать (сколько бы ни было - все впустую) можно сделать еще хитрее: перед тем, как нагрузить потоки установить таймер, для того, чтобы не забыть о своих дескрипторах прикольно, а если у нас много соединений? система захлебнется от нагрузки предлагаю каждому из нас написать простой echo-сервер и сравнить производительность
а что собственная реализация? Код (Text): while (1) { if ((count = epoll_wait(epoll_fd, &events, 2500, 0)) > 0) handle_owner_events(&events); else if (!count) help_other_thread(); } epoll_wait() неблокирующий, возвращает управление мгновенно если событий нет, нагружаем поток, чтобы он не простаивал за то время, пока пакет дойдет до очереди входных пакетов сокета, можно сделать кучу работы можно прикинуть сколько времени пакет идет от NIC к сокету и в help_other_thread() периодически проверять, не превышен ли лимит, например через TSC итого, реакция практически мгновенная
rei3er Низзя. Там AFAIK означает примерно следующее: As Far As I Can Imagine. Пример про кучу соединений показательный, но должен быть действительно хитро организован. И эти хитрости, например оценка эффективности распределения части соединений на другие потоки, тоже займут время. В результате придется постоянно динамически распределять самостоятельно по потокам обработчики событий так, чтобы CPU были загружены равномерно, а организация такого механизма опять таки вряд ли будет менее требовательной к ресурсам, чем тот же планировщик потоков. Мало того, Вы рассматриваете ситуацию, когда потока всего четыре (сколько и CPU), но в случае блокирующих сокетов используются как раз столько же потоков, сколько и соединений. В результате те потоки, на сокетах которых не было событий будут стоять, но вот CPU простаивать не будут, т.к. система должна постараться равномерно распределить всю кучу потоков, на сокетах которых произошли события, по четырем CPU. А их нет согласно задаче. Боюсь. Тем более, что в моем сервере ограничение на количество соединений - двадцать. Я же с самого начала говорил о том, что потоков не должно быть слишком много, иначе системе можно защемить что-нибудь чувствительное и тогда производительность может упасть у всего.
rei3er Мда. Пока свой пост отправил, два новых появилось. Про poll и epoll_wait я вообще впервые слышу. А вот функция help_other_thread() - это круто. Вот тут и пойдут хитрости. Other thread должен как минимум знать о том, что ему кто-то собирается help, а у Вас он будет самостоятельно handle_owner_events. mChief Вы еще даже с протоколом не определились, а уже длину сообщения считаете. Наверное, подразумевается по умолчанию TCP, тогда МАКСИМАЛЬНУЮ длину сообщений (фиксированная просто длина сообщений выглядит примерно так: сообщение "Привет" передаётся в контексте бессмысленных данных так, чтобы все сообщение занимало 4 КБ) практически нет смысла фиксировать.
равномерность здесь абсолютно не нужна (в том плане, что ничего распределять не надо) важно, чтобы потоки не простаивали сходу приходит на ум общая очередь событий epoll_wait() -> проверка отсутствия событий -> запись в общую очередь -> обработка -> epoll_wait() синхронизация очереди неблокируемая (lock cmpxchg) с задачей же уже разобрались
общая очередь событий никакой поток не знает (и не должен знать), что ему кто-то будет помогать т. е каждый поток расшаривает для других потоков то, что он собирается обрабатывать здесь труднее всего организовать неблокирующую синхронизацию
l_inc используемый протокл забыл сказать т.к. изначально собирался использовать ТСР и вопрос выбора не стоял.
rei3er Ну я все время писал в контексте задачи. Иначе как-то это не очень хорошо получается: "С задачей в этой теме разобрались - теперь можно и за жисть пофлудить". Надо прекращать. Как же общая, если на каждый поток предполагалось определить по 2500 вполне конкретных сокетов?
Пробую писать с неблокирующими сокетами. Как я понял, клиента можно считать отключившимся, если функции recv и send постоянно возвращают ошибку. Если это так, то какие коды ошибок проверять?
мне код писАть что-ли? схематично вот Код (Text): static struct list_head queue; // общая очередь ... static __thread struct epoll_event events[2500]; // TLS массив для хранения событий для каждого из 2500 сокетов static __thread eventpoll_fd; // TLS дескриптор объекта eventpoll ... // цикл в каждом из 4-х потоков while (1) { if ((count = epoll_wait(eventpoll_fd, events, 2500, 0)) < 0) { // error } add_events(events, &queue); if (count > 0) handle_events(events); // работаем только со своими дескрипторами else help_threads(); } ... static void help_threads() { struct list_head * iterator; struct epoll_event * event; list_for_each(iterator, &queue) { event = list_entry(iterator); // работаем с событием (синхронизация прилагается) } }
rei3er Лучше на этой глубокой мысли и остановимся. mChief В MSDN о том, когда клиента считать отключившимся и какие ошибки учитывать, вполне прилично написано.