async и await нужны не для того, чтобы любая программа стала быстрее. Они нужны, чтобы удобно писать код, который ждет: HTTP-запрос, файл, базу данных, таймер, внешний сервис. В этом уроке сделаем маленький пример с загрузкой профиля пользователя и разберем Task, async, await без мистики
Главная мысль: await не «создает поток». Он позволяет методу приостановиться, пока операция не завершится, и продолжить выполнение позже
Что получится в конце
Программа выведет:
Загружаю профиль...
Профиль: Алия
Готово
Код будет похож на обычный последовательный код, хотя внутри есть ожидание
Создаем проект
dotnet new console -n CSharpAsync
cd CSharpAsync
В Program.cs:
Console.WriteLine("Загружаю профиль...");
var profile = await LoadProfileAsync(1);
Console.WriteLine($"Профиль: {profile.Name}");
Console.WriteLine("Готово");
static async Task<UserProfile> LoadProfileAsync(int userId)
{
await Task.Delay(1000);
return new UserProfile(userId, "Алия");
}
public record UserProfile(int Id, string Name);
Запустите:
dotnet run
Современный C# позволяет использовать await в top-level statements, поэтому отдельный static async Task Main() в первом примере не нужен
Что такое Task
Task<UserProfile> означает: операция когда-нибудь завершится и даст UserProfile
static async Task<UserProfile> LoadProfileAsync(int userId)
Метод не возвращает профиль сразу. Он возвращает задачу, которую можно ожидать:
var profile = await LoadProfileAsync(1);
После await в переменной profile будет уже результат, а не сама задача
Если метод ничего не возвращает, но тоже асинхронный, обычно пишут:
static async Task SaveAsync()
Не void, а Task. async void оставляют почти только для обработчиков событий в UI
Что делает await
Строка:
await Task.Delay(1000);
имитирует ожидание в одну секунду. В реальном коде вместо этого может быть чтение файла, запрос к API или обращение к базе данных
await говорит: дождись завершения операции, а потом продолжай метод. Поэтому код читается сверху вниз:
- Напечатали «Загружаю профиль…».
- Дождались профиля.
- Напечатали имя.
- Напечатали «Готово».
Обработка ошибок в async-коде
Ошибки в асинхронных методах обрабатываются обычным try/catch:
try
{
var profile = await LoadProfileAsync(0);
Console.WriteLine(profile.Name);
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Ошибка: {ex.Message}");
}
Изменим метод:
static async Task<UserProfile> LoadProfileAsync(int userId)
{
await Task.Delay(1000);
if (userId <= 0)
{
throw new InvalidOperationException("Некорректный ID пользователя");
}
return new UserProfile(userId, "Алия");
}
Когда задача завершается с ошибкой, await достает эту ошибку и бросает ее в месте ожидания. Поэтому try/catch вокруг await работает естественно
Где async не помогает
Если задача чисто вычислительная и быстро выполняется на CPU, async/await сам по себе не ускорит ее
Плохой пример:
static async Task<int> SumAsync(int a, int b)
{
return a + b;
}
Здесь нечего ждать. Обычный метод лучше:
static int Sum(int a, int b) => a + b;
Асинхронность полезна там, где есть ожидание внешней операции
Частые ошибки
Забыли await. Тогда в переменной окажется Task, а не результат. Компилятор часто подсказывает это сразу
async void. Не используйте для обычных методов. Ошибки из async void сложнее контролировать
Блокировка через .Result или .Wait(). В учебном консольном коде иногда работает, но в приложениях может приводить к блокировкам. Лучше распространять async вверх и использовать await
Название без Async. Это не ошибка компиляции, но стандарт .NET рекомендует добавлять Async к асинхронным методам
Что может быть еще интересно по этой теме
Async делает код параллельным? Не обязательно. Он делает ожидание неблокирующим и удобным для чтения. Параллельность — отдельная тема
Task и Thread это одно и то же? Нет. Task представляет операцию, а не обязательно отдельный поток
Нужно ли async в ASP.NET Core? Да, при работе с базой, файлами и внешними API. Это помогает серверу не держать поток без дела во время ожидания
Почему почти все библиотеки имеют методы с Async? Потому что I/O-операции лучше ждать асинхронно, особенно в серверных приложениях
Что открыть дальше
- JSON и файлы в C#: читаем настройки проекта — там чтение можно будет сделать асинхронным.
- ASP.NET Core Minimal API: первый endpoint — async часто встречается в web endpoints.
- List, Dictionary и LINQ в C# на живом примере — коллекции пригодятся для подготовки данных.
- Ошибки C#: cannot convert, null reference и missing using — чтобы не теряться в сообщениях компилятора.



