select () - это путь, для которого он и создан. Если вы выберете многопоточный неблокирующий подход, вам придется реализовывать спящий режим после каждого тика, иначе поток будет использовать все доступное время процессора. Таким образом, наихудшее время отклика, если один клиент отправляет данные, является вашим значением сна. Вы также можете реализовать один поток на сокет и сделать его блокирующим, но в зависимости от того, сколько сокетов у вас есть, это будет очень затратно.
С помощью select () вы можете просматривать все сокеты одновременно (независимо от того, блокируют они или нет, кстати) и должны обрабатывать только те из них, которые активны.
Если вы работаете в Linux и у вас есть много сокетов для просмотра, вы можете взглянуть на epoll ()