Подтвердить что ты не робот

Поток за соединение против шаблона реактора (с пулом потоков)?

Я хочу написать простую многопользовательскую игру как часть моего учебного проекта на С++.

Поэтому я подумал, что, поскольку я нахожусь в этом, я хотел бы сделать это правильно, а не просто делать это.

Если я правильно понял: Apache использует архитектуру Thread-per-connection, а nginx использует цикл событий, а затем выделяет рабочего [x] для входящего подключение. Думаю, nginx мудрее, так как он поддерживает более высокий уровень concurrency. Правильно?

Я также столкнулся с этой умной аналогией, но я не уверен, можно ли ее применить к моей ситуации. Аналогия также кажется очень идеалистической. Я редко видел, что мой компьютер работает на 100% -ном процессоре (даже с открытыми вкладками Chrome, Photoshop и тем, что не работает одновременно)

Кроме того, я столкнулся с SO-сообщением (как-то он исчез из моей истории), когда пользователь спросил, сколько потоков они должны использовать, и один из ответов заключался в том, что вполне приемлемо иметь около 700, даже до 10 000 потоки. Однако этот вопрос был связан с JVM.

Итак, давайте оценим вымышленную пользовательскую базу около 5000 пользователей. Какой подход должен быть "наиболее параллельным"?

  • Образец реактора работает все в одном потоке.
  • Схема реактора с пулом потоков (примерно, насколько велика вы предполагаете, что пул потоков должен быть?
  • Создание потока для каждого соединения, а затем уничтожение потока, которое соединение закрывается.

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

PS: Я рассматриваю возможность использования POCO С++ Libraries. Предлагая любые альтернативные библиотеки (например, boost), я в порядке. Однако многие говорят, что библиотека POCO очень чиста и понятна. Поэтому я бы предпочел использовать этот, поэтому я могу узнать о том, что я использую.

4b9b3361

Ответ 1

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

  • Никогда не блокировать реактивную нить:
    • Любая блокировка серьезно ухудшит производительность вашего сервера, вы обычно используете небольшое количество реактивных потоков, поэтому блокирование также может быстро вызвать тупик.
    • Нет мьютексов, поскольку они могут блокироваться, поэтому нет общего измененного состояния. Если вам требуется совместное использование, вам придется обернуть его актером или похожим, так что только один поток имеет доступ к состоянию.
  • Все работы в реактивных потоках должны быть связаны cpu
    • Все IO должны быть асинхронными или выполняться в другом пуле потоков, а результаты возвращаются в реактор.
    • Это означает использование фьючерсов или обратных вызовов для обработки ответов, этот стиль кода может быстро стать недостижимым, если вы не привыкли к нему и не дисциплинированы.
  • Все работы в реактивных потоках должны быть небольшими
    • Чтобы поддерживать отзывчивость сервера, все задачи в реакторе должны быть небольшими (ограниченными временем)
    • На 8-ядерном компьютере вы не можете не допустить, чтобы 8 длинных задач приходилось в то же самое время, потому что никакая другая работа не начнется, пока они не будут завершены.
    • Если задачи могут занять много времени, их необходимо разбить (совместная многозадачность)

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

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

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

  • Какая единица работы?
  • Как легко добавить новую работу? может ли он поступать только от внешнего события (например, сетевого запроса).
  • Как легко разбить работу на более мелкие куски?
  • Насколько легко обрабатывать результаты этой работы?
  • Насколько легко перемещать блокирующий код в другой пул потоков и обрабатывать результаты?

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

Желаем удачи, познакомившись с С++ и с которым когда-либо вы делаете выбор.

Ответ 2

Вариант 2 наиболее эффективно займет ваше оборудование. Вот классическая статья, десять лет, но все же хорошая.

http://www.kegel.com/c10k.html

Лучшая комбинация библиотек в эти дни для структурирования приложения с concurrency и асинхронным ожиданием - Boost Thread plus Boost ASIO. Вы также можете попробовать библиотеку С++ 11 std thread и std mutex (но Boost ASIO во многих случаях лучше, чем мьютексы, просто всегда обратный вызов в тот же поток, и вам не нужны защищенные области). Держитесь подальше от std future, вызывают его нарушение:

http://bartoszmilewski.com/2009/03/03/broken-promises-c0x-futures/

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

Ответ 3

FWIW, Poco поддерживает вариант 2 (ParallelReactor) с версии 1.5.1

Ответ 4

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

Ответ 5

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

игровые серверы (как правило) выполняют много операций ввода-вывода и относительно немного вычислений, поэтому они далеки от 100% -ных приложений. с другой стороны, они также обычно изменяют значения в некоторой базе данных (модель "игрового мира" ). все игроки создают записи и записи в эту базу данных. которая в точности является проблемой пересечения в аналогии.

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

поэтому вариант 1 или 2 допустим в вашей ситуации. по причинам масштабируемости я бы не рекомендовал вариант 3.