В чем разница между CPU-bound и IO-bound потоками
Что проверяют
Понимаете ли вы, что эти две нагрузки лечатся разными инструментами, и не путаете ли асинхронность с параллельностью. Почти всегда следом спрашивают, зачем в ASP.NET Core оборачивать работу в Task.Run и нужно ли это вообще.
Как отвечать
Начните с того, где тратится время:
- CPU-bound: поток занят вычислениями, узкое место в процессоре. Парсинг больших файлов, сжатие, криптография, обработка изображений. Ускоряется распараллеливанием по ядрам:
Parallel.For,Parallel.ForEach, PLINQ (AsParallel()),Task.Runдля выноса тяжёлой работы с UI-потока; - IO-bound: время уходит на ожидание сети, диска или базы. Лечится не потоками, а асинхронным вводом-выводом:
await httpClient.GetAsync(...),ReadAsync,ToListAsync()в EF Core. Пока идёт ожидание, поток не занят: он возвращается в пул и обслуживает другие запросы, а продолжение выполнится по сигналу от ОС (IOCP в Windows, epoll в Linux).
Отсюда главное правило: для IO-bound нужен настоящий асинхронный API, а не Task.Run вокруг синхронного вызова. Такая обёртка просто занимает другой поток, который будет стоять и ждать.
Для CPU-bound в веб-приложении Task.Run почти ничего не даёт: запрос и так выполняется на потоке пула, вы лишь меняете один поток пула на другой. Вынос имеет смысл в десктопе (WPF, WinForms), чтобы не замораживать интерфейс, или когда тяжёлую задачу отдают в фоновую очередь.
Для смешанной задачи, например скачать тысячу файлов и обработать каждый, подходит Parallel.ForEachAsync с ограничением ParallelOptions.MaxDegreeOfParallelism.
Где спотыкаются
Вызывают .Result или .Wait() на асинхронном методе. Под нагрузкой это блокирует потоки пула, пул наращивает их медленно, и сервис упирается в thread pool starvation при почти пустом процессоре.
Передают асинхронную лямбду в Parallel.ForEach. Она превращается в async void, цикл не ждёт завершения операций, а исключения теряются.
Считают, что async сам по себе делает код быстрее. Для вычислительной задачи он добавляет накладные расходы на машину состояний и ничего не ускоряет.