1) Смотрите, если у вас асинхронная работа (вы пишете работа с сокетами), то создавать много потоков, нецелесообразно, так как за потоком подразумевается работа процессора, а при работе с сокетами, этот поток будет большую часть времени простаивать.
2) Таски используют пул потоков, поэтому особой разницы между ThreadPool.QueueUserWorkItem и запуском Таски нет.
3) Что Thread.Sleep(10000) что Task.Delay(10000).Wait() - это блокирующая операция, которая приводит к тому что называют thread pool starvation. То есть поток не имеет возможности вернуться в пул и переиспользоваться. У пула потоков есть задержка на ввод нового потока в работу, поэтому у вас все и тормозит.
Вывод в том, что вы некорректно готовите асинхронность. Во всех случаях вы блокируете потоки и количество потоков у вас во всех случаях примерно одинаковое задействуется. Если вы ограничены 4.0, то скорее всего, лучшим вариантом будет старая модель асинхронности .net
https://docs.microsoft.com/en-us/dotnet/standard/a...
У сокетов есть соответствующие методы