Показать сообщение отдельно
Старый 01.11.2016, 20:21   #15
h1dd3n
Бывалый
 
Аватар для h1dd3n
 
Регистрация: 19.06.2008
Сообщений: 679
Написано 264 полезных сообщений
(для 450 пользователей)
Ответ: Правильная организация Server<-> client

Судя по всему в этой теме смешалось в кучу все: кони, люди, потоки, таски, 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.
__________________
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Andvrok (01.11.2016)