Что лучше, однопоточный или многопоточный сервер? - PullRequest
5 голосов
/ 11 мая 2011

Мне нужно создать простое клиентское <-> соединение с сервером для передачи файлов с использованием языка Си (Linux).

Сервер принимает соединения через порт 10000, я не знаю, лучше ли каждый раз создавать новый поток или создавать фиксированное число потоков и использовать асинхронную технику.

CASE A:

client --> server --> (new thread) --> process the request

CASE B:

SERVER --> create thread 1 - thread 2 - thread 3

then

client1 --> server --> thread 1
client2 --> server --> thread 2
client3 --> server --> thread 3
client4 --> server --> thread 1
client5 --> server --> thread 2
client6 --> server --> thread 3

В этом случае поток 1 может обработать множество клиентских запросов

Мои соображения:

Случай 1: Быстрее, но тратит много памяти

CASE 2: Работает медленнее, но использует мало памяти

Я не прав?

Ответы [ 7 ]

5 голосов
/ 11 мая 2011

Если вы подумаете о проверке архитектуры широко используемых http-серверов (nginx, lighttpd, apache), вы заметите, что те, которые используют фиксированное количество потоков (так называемые «рабочие потоки», их количество должно зависеть от количества процессоров на сервере): намного быстрее, чем один, использующий большой пул потоков. Однако есть очень важные моменты:

  1. Реализация "рабочего потока" не должна быть такой простой, как она заманчива, иначе не будет никакого реального прироста производительности. Каждый поток должен реализовать каждый псевдо-параллелизм, используя конечный автомат, обрабатывая несколько запросов одновременно. Здесь запрещены никакие операции блокировки - например, время в потоке, которое ожидает ожидания ввода-вывода с жесткого диска для получения содержимого файла, может использоваться для анализа запроса следующего клиента. Это довольно сложный код для написания, однако.

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

  3. Как вы можете заметить, решения «рабочих потоков» сами обслуживают только статические данные, они передают динамическое выполнение сценариев некоторым другим программам. Насколько я знаю (может быть неправильно), это связано со сложностями неблокирующей обработки запроса с некоторыми неизвестными динамическими вещами, выполняемыми в их контексте. В любом случае, это не должно быть проблемой в вашем случае, поскольку вы говорите о простой передаче файлов.

Причина, по которой решение с ограниченными потоками является более быстрым в системах с большой нагрузкой - поток http://en.wikipedia.org/wiki/Context_switch является довольно дорогостоящей операцией, так как требует сохранения данных из регистров и загрузки нового, при условии, что некоторые другие локальные данные потока , Если у вас слишком много потоков по сравнению с числом процессов (например, в 1000 раз больше), много времени в вашем приложении будет потрачено впустую, просто переключаясь между потоками.

Итак, короткий ответ на ваш вопрос: «Нет, это не имеет никакого отношения к использованию памяти, выбор зависит от типа обслуживаемых данных, запланированного количества запросов / секунд и способности тратить много времени на кодирование».

2 голосов
/ 11 мая 2011

Мой первый выбор - сделать это однопоточным, используя select (2). Если бы это было недостаточно хорошо с точки зрения производительности, я бы выбрал решение для пула потоков. Это будет лучше масштабироваться.

Бывают случаи, когда создание одного потока для каждого клиента совершенно нормально. Я сделал это, и это работало хорошо для этого приложения, обычно около 100 клиентов, максимум до 1000 клиентов. Это было 15 лет назад. Сегодня одно и то же приложение может обрабатывать 10000 клиентов благодаря более качественному оборудованию. Просто помните, что один поток на клиента не очень хорошо масштабируется.

2 голосов
/ 11 мая 2011

Здесь нет правильного ответа. Зависит от многих вещей. И вам нужно выбрать самостоятельно.

"СЛУЧАЙ 1: быстрее, но тратит много памяти
«Случай 2: медленнее, но используется мало памяти»

Неправильно. Зависит от многих вещей. Создание потоков не так уж и дорого (но не так уж много), но если потоков будет слишком много, у вас возникнут проблемы.

Это очень сильно зависит от нагрузки - какова ожидаемая нагрузка? Если это, скажем, около 1000 запросов в секунду - вы знаете, если вы создаете 1000 потоков в секунду ..... это будет катастрофа: D

Также - создайте столько потоков, сколько сможет обработать процессор, без (большого) переключения между ними. Существует большая вероятность (конечно, зависит от вашей программы), что одноядерный процессор будет работать намного медленнее с 10 потоками вместо 1 (или 2). Это действительно зависит от того, что будут делать эти темы.

Я бы выбрал создание пула потоков и повторное использование потоков.

1 голос
/ 28 января 2016

Я знаю, что прошло довольно много времени с тех пор, как вы спросили об этом, но вот мой взгляд на ваш вопрос с точки зрения человека, который уже написал несколько серверов на C.

Если ваш сервер является вашим собственным, полностью зависимым и независимым от других кодов, я бы настоятельно рекомендовал бы вам сделать это однопоточным с неблокирующими сокетами и используя epoll (Linux), kqueue (BSD) или WSAEventSelect (Windows).

Для этого может потребоваться разбить код, который в противном случае был бы «простым», на гораздо более мелкие куски, но если вам нужна масштабируемость, это побьет все серверы на основе потоков / выбора.

Однажды была замечательная статья под названием «Проблема C10K», которая полностью посвящена проблеме обработки 10 000 одновременных соединений. Я действительно многому научился из этого, вот ссылка на него: http://www.kegel.com/c10k.html.

Существует также еще одна замечательная статья, посвященная масштабируемости, которая называется «Масштабируемая сеть», которую вы можете найти здесь: http://bulk.fefe.de/scalable-networking.pdf.

Эти два отличных чтения, надеюсь, это поможет.

0 голосов
/ 11 мая 2011

Это на самом деле зависит от того, что делает ваш сервер.

Я бы порекомендовал вам сделать самое простое из возможных. Вероятно, это модель с одним процессом, которая мультиплексирует все доступные соединения, используя, select, poll, libevent или аналогичные.

То есть, если вы используете TCP.

Если вы используете UDP, это даже проще, поскольку приложение может делать все с одним сокетом, поэтому оно может (возможно) использовать блокирующий сокет.

0 голосов
/ 11 мая 2011

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

Существует проект типа "пул потоков" здесь с использованием pthreads.Возможно, вы сможете получить некоторые идеи о том, как реализовать.

0 голосов
/ 11 мая 2011

Это полностью до вас.Нет правильного или неправильного ответа.Вы уже определили плюсы и минусы обоих, и вы правы с обоими;1 быстрее, но интенсивнее, 2 медленнее, потому что клиентам, возможно, придется ждать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...