Имеет ли смысл использовать синхронность для тяжелых операций в C#
Что проверяют
Различаете ли вы задачи, упирающиеся в процессор, и задачи, упирающиеся в ожидание. От этого зависит правильный инструмент, и async тут помогает не всегда.
Как отвечать
Сразу разделите два случая.
Тяжёлая вычислительная работа (разбор большого файла, обработка изображения, расчёт) не становится быстрее от async. Асинхронность освобождает поток на время ожидания, а тут ожидания нет, работа занимает процессор. Значит:
- в консольном приложении или фоновой службе синхронный вызов вполне уместен, а параллелизм даёт
Parallel.ForилиPLINQ; - в интерфейсном приложении синхронный вызов недопустим: он заморозит окно. Работу уводят в пул через
Task.Runи ждут результат черезawait; - в веб-приложении
Task.Runобычно вреден: поток всё равно берётся из того же пула, который обслуживает запросы, и вы просто перекладываете нагрузку с одного потока на другой, добавив накладные расходы. Тяжёлые вычисления там выносят в фоновую службу или в отдельный сервис.
Операции ожидания (сеть, база, диск) наоборот должны быть асинхронными: поток освобождается и обслуживает другие запросы. Синхронный вызов здесь съедает поток из пула, и под нагрузкой это приводит к исчерпанию пула и резкому росту задержек.
Отсюда правило: async для ввода-вывода, параллелизм для вычислений, синхронный код там, где нет ни того, ни другого.
Где спотыкаются
Оборачивают вычисления в Task.Run внутри контроллера и считают, что сделали сервис асинхронным.
Вызывают асинхронный метод синхронно через .Result и получают взаимную блокировку в контексте с синхронизацией.
Считают, что async ускоряет код: он влияет на использование потоков, а не на скорость самой работы.