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

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

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

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

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

Кроме того, я натолкнулся на SO сообщение (каким-то образом оно исчезло из моей истории), где пользователь спросил, сколько потоков ему следует использовать, и один из ответов был таков: вполне приемлемо иметь около 700, даже до 10000 потоков. Этот вопрос был связан с JVM, хотя.

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

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

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

PS: я рассматриваю возможность использования POCO C ++ Библиотеки. Предлагая любые альтернативные библиотеки (например, увеличение) хорошо со мной. Однако многие говорят, что библиотека POCO очень чистая и понятная. Итак, я бы предпочел использовать это, чтобы я мог узнать о какиз того, что я использую.

13

Решение

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

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

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

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

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

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

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

Желаем удачи в изучении C ++ и с каким выбором вы когда-либо делали.

12

Другие решения

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

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

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

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

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

7

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

2

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

1

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

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

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

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

1
По вопросам рекламы [email protected]