Что такое асинхронный / ожидающий эквивалент сервера ThreadPool?

Я работаю на tcp-сервере, который выглядит примерно так, используя синхронный apis и пул потоков:

TcpListener listener;
void Serve(){
  while(true){
    var client = listener.AcceptTcpClient();
    ThreadPool.QueueUserWorkItem(this.HandleConnection, client);
    //Or alternatively new Thread(HandleConnection).Start(client)
  }
}

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

Мое первоначальное впечатление что-то вроде:

async Task Serve(){
  while(true){
    var client = await listener.AcceptTcpClientAsync();
    HandleConnectionAsync(client); //fire and forget?
  }
}

Но меня поражает, что это может вызвать узкие места. Возможно, HandleConnectionAsync потребуется необычно много времени, чтобы достичь первого ожидания, и остановит выполнение основного цикла приема. Будет ли при этом использоваться только один поток, или среда выполнения будет магическим образом запускать вещи в нескольких потоках так, как считает нужным?

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

Есть ли идиоматический способ максимизировать пропускную способность в такой ситуации?

Ответы на вопрос(5)

Ваш ответ на вопрос