Судя по всему в этой теме смешалось в кучу все: кони, люди, потоки, таски, async/await, threadpool...
- Создавать потоки вручную (использовать класс Thread) в современном C#/.NET нужно только в 0.00001% случаях, и ситуация ТС под этот случай не подходит.
- В C# нынче (и вообще-то уже довольно давно) оперируют тасками - Task, Task<T>. Таск это просто задача, сами по себе таски никакого отношения к потокам не имеют. Таски вообще совершенно отдельная от async/await фича.
- Для того чтобы сервер нормально масштабировался хорошо бы чтобы потоки не блокировались и переиспользовались. Async/await к этому отношения не имеет. Асинхронные серверы были в дотнете ЕМНИП со 2.0 версии фреймворка - IAsyncResult паттерн.
- Когда мы создаем таск (Task), то скорее всего (такое поведение по умолчанию), мы ставим эту задачу на выполнение шедулеру (TaskScheduler).
- Шедулер по своему внутреннему алгоритму как-то выполняет задачи. Дефолтный шедулер использует ThreadPool. В дотнете есть и другие шедулеры. Можно также написать свой шедулер. Но это не понадобится (скорее всего).
- Async/await - это просто синтаксический сахар C# (на уровне clr об async/await даже не нужно ничего знать). Самое главное понять что он никакой магии не делает.
- Использовать Thread.Sleep(10) - гиблое дело. Надо использовать await Task.Delay(10). То есть пока мы ждем эти 10мс, поток может пойти и выполнить какую-нибудь работу.
- При использовании TPL (таски, шедулер и Ко) совсем не обязательно что продолжение будет выполняться тем же потоком. То есть: есть 2 задачи - прочитать из 1 сокета, и обработать, и прочитать из 2 сокета и обработать. Предположим действие происходит следующим образом: поток A обрабатывает первую задачу, он доходит до чтения и дальше ничего делать не может пока данные не придут. Конечно он идет делать другую полезную задачу - обрабатывать задачу 2. Но пока он это делал в 1 задаче из i/o пришли данные и надо бы выполнение то продолжить, но т.к. поток А занят, этим займется поток Б (который возьмется из тредпула).
- Пункт выше объясняет почему создавать потоки вручную - идиотизм. Может получиться так что: создали поток, внутри потока, где-то есть await. Значит все продолжение после await может быть выполнено потоком из тредпула. Получается что созданный вручную поток потерян навсегда и вообще не будет использоваться.
Тебе надо - создать таск который в бесконечном цикле принимает соединения. Когда принимаешь соединение создаешь таск с бесконечным циклом в котором принимаешь данные.
Далее есть 2 варианта - если у тебя архитектура запрос/ответ (ну как http примерно) то сразу же начинаешь обрабатывать. Если у тебя что-то типа реалтайм сервера, то ты полученные данные просто куда-то кладешь (в какое-то потокобезопасное хранилище).
Нету никакого "шага" в виде 10мс на чтение. Читаешь сколько можешь.
Вот на обработку (если мы говорим о реалтайм сервере) уже может быть шаг предположим 8мс. Каждый раз в этом реалтайм цикле (а это тоже таск, не поток), ты берешь все данные из этого "потокобезопасного хранилища" и обрабатываешь их, потом снова уходишь в Task.Delay.