Ответ
Выполнение продолжается в методе, который вызвал await, после того как ожидаемая задача (Task или Task<T>) завершится. Механизм основан на конечных автоматах, генерируемых компилятором.
Что происходит под капотом:
- При встрече ключевого слова
awaitвыполнение текущего метода приостанавливается, если задача еще не завершена. - Управление возвращается вызывающему методу (позволяя потоку не блокироваться).
- Когда задача завершается (например, приходит ответ от HTTP-запроса), выполнение исходного метода возобновляется с точки останова.
- Контекст синхронизации (например, UI-поток в WPF/WinForms) сохраняется и используется для продолжения по умолчанию, если не указано иное с
ConfigureAwait(false).
Практический пример:
public async Task<string> FetchDataAndProcessAsync()
{
Console.WriteLine("1. Начинаем запрос...");
// Выполнение приостанавливается здесь, пока запрос не завершится.
// Поток освобождается для другой работы.
string data = await httpClient.GetStringAsync("https://api.example.com/data");
// ! Выполнение продолжится здесь ТОЛЬКО после завершения GetStringAsync.
// Для UI-приложения это будет тот же UI-поток.
// Для консольного/веб-приложения это может быть любой поток из пула.
Console.WriteLine("2. Данные получены, начинаем обработку...");
string processedData = Process(data); // Синхронная обработка.
Console.WriteLine("3. Обработка завершена.");
return processedData;
}
// Вызывающий код
public async Task MainLogic()
{
var task = FetchDataAndProcessAsync(); // Задача запущена.
Console.WriteLine("В то время как запрос выполняется, можно делать другую работу...");
DoOtherWork(); // Эта работа выполняется ПАРАЛЛЕЛЬНО с HTTP-запросом.
// Ожидаем результат, если он нужен.
string result = await task; // Здесь также может быть пауза, если задача не завершена.
Console.WriteLine("4. Результат получен в основном потоке логики: " + result);
}
Критически важные моменты:
- Не блокируйте асинхронный код: Не используйте
.Resultили.Wait()на задачах, особенно в UI-потоках или ASP.NET Core контексте — это может привести к взаимоблокировке (deadlock). - Используйте ConfigureAwait(false): В библиотечном коде, чтобы избежать привязки к конкретному контексту синхронизации и улучшить производительность.
- Исключения: Исключения, выброшенные в асинхронной операции, "пробрасываются" в точку
awaitпри возобновлении метода.