Я предполагаю, что вы не говорите о наличии потока для каждого клиента, поскольку такой дизайн обычно по совершенно разным причинам, а скорее пул потоков, каждый из которых обрабатывает несколько одновременных клиентов.
Причиной такой архитектуры по сравнению с однопоточным сервером является простота использования нескольких процессоров. Вы делаете больше работы, чем просто ввод / вывод. Вы должны анализировать сообщения, выполнять различную работу, возможно, даже запускать более мощные криптоалгоритмы. Все это занимает процессор. Если вы хотите масштабировать, использование преимуществ нескольких процессоров позволит вам масштабировать еще больше и / или сохранить задержку еще ниже для каждого клиента.
Некоторая выгода в таком дизайне может быть немного скомпенсирована тем фактом, что вам может потребоваться дополнительная блокировка в многопоточной среде, но все сделано правильно, и, безусловно, в зависимости от того, что вы делаете, это может быть огромной победой за счет большей сложности.
Кроме того, это может помочь преодолеть ограничения ОС. Пути ввода / вывода в ядре могут быть более распределены между процессорами. Не все операционные системы могут в полной мере поддерживать поток ввода-вывода из однопоточных приложений. В прежние времена существовали не все отличные альтернативы старому * nix select (), который обычно имел ограничение на количество файловых дескрипторов 1024, и подобные API-интерфейсы начали серьезно деградировать, как только вы сказали ему контролировать слишком много сокетов. Распределение всех этих клиентов по нескольким потокам или процессам помогло преодолеть это ограничение.
Что касается отображения 1: 1 между потоками, есть несколько причин для реализации этой архитектуры:
Более простая модель программирования, которая может привести к менее трудному поиску ошибок и более быстрой реализации.
Поддержка API блокировки. Это повсюду. Наличие потока, управляющего многими / всеми клиентами, а затем выполнение блокирующего вызова базы данных остановит всех. Даже чтение файлов может блокировать ваше приложение, и вы обычно не можете отслеживать обычные файловые дескрипторы / дескрипторы для событий ввода-вывода - или, когда вы можете, модель программирования часто исключительно сложна.
Недостатком здесь является то, что он не будет масштабироваться, по крайней мере, не с наиболее широко используемыми языками / фреймворком. Наличие тысяч собственных потоков ухудшит производительность. Хотя некоторые языки предоставляют гораздо более легкий подход, например, Erlang и в некоторой степени Go.