Task — это обёртка над потоком

Я — «Папочка Разработки»: пишу о карьере .NET/C#, собесах и том, как продавать свои навыки дороже. Без воды: разборы утечек памяти, практические гайды по интервью, резюме с цифрами и честные кейсы с рынка. Плюс немного про ИИ — как делегировать ему рутину и ускорять работу. Заходи, если нужен понятный план действий, а не мотивационные лозунги.

taskпотокasync

Если в это верить, то тысяча задач — тысяча потоков. И начинается: задачи боятся создавать, запросы к API заворачивают в Task.Run, чтобы точно ушли в фон, а любой WhenAll кажется миной.

Я проверил. Запустил разом 10 000 задач:

var tasks = Enumerable.Range(0, 10_000)
    .Select(_ => Task.Delay(1000))
    .ToArray();
await Task.WhenAll(tasks);

Всё закончилось за 1029 мс. Потоков в процессе в это время было 12, из них в пуле два 🥰

Task — квитанция из химчистки. Тебе дали бумажку: результат будет, приходи потом. Кто стирает и стирает ли вообще прямо сейчас, в квитанции не написано. С Task.Delay никто не стирает: есть таймер, который через секунду скажет "готово". С запросом к базе то же самое, ответа ждёт сетевая карта и ОС, а не твой поток.

Поток — работник. Task — обещание результата. Иногда за обещанием стоит работник (Task.Run), а иногда его там нет совсем.

Так что вот это ничего не даёт, кроме лишнего прыжка в пул:

await Task.Run(() => _http.GetStringAsync(url));

HTTP-запрос и без него не держит поток, пока ждёт ответ.

Поищи у себя в проекте Task.Run вокруг HttpClient. Я подожду 🫡

Я сделал тест по асинхронности и многопоточности у себя на dotnetdad: 10 вопросов, минут на пять. В конце разбор ответов и уроки ровно по тем местам, где ошибся 🌟

Читайте так же