Что такое async и await
В C# ключевые слова async и await используются для асинхронного программирования, позволяя писать неблокирующий код, который работает с длительными операциями (например, запросами к сети, работе с файлами или базой данных) без создания отдельных потоков вручную и без «адских» колбэков.
🔹 Что такое async?
Ключевое слово async используется для объявления метода как асинхронного. Это означает, что метод может содержать await-выражения и будет выполняться асинхронно, позволяя вызывающему коду не блокироваться.
Метод, помеченный async, должен возвращать:
- Task<T> — если есть результат
- Task — если ничего не возвращает
- ValueTask / ValueTask<T> — с C# 7.0, для оптимизации
- void — только для обработчиков событий
Пример:
public async Task<int> GetDataAsync() {
// имитация долгой операции
await Task.Delay(1000);
return 42;
}
🔸 Что такое await?
Ключевое слово await используется внутри метода, помеченного async, и указывает, что выполнение должно приостановиться, пока не завершится указанная задача (Task).
int result = await GetDataAsync();
Когда вызывается await, текущий поток не блокируется — он возвращается вызывающему коду, а метод возобновляется позже, когда задача завершится.
📦 Пример: асинхронный HTTP-запрос
public async Task<string> DownloadPageAsync(string url) {
using HttpClient client = new HttpClient();
string content = await client.GetStringAsync(url);
return content;
}
- client.GetStringAsync(url) возвращает Task<string>.
- await ждёт завершения этой задачи не блокируя поток.
- Метод продолжает выполнение, когда результат получен.
🧠 Что происходит под капотом?
Метод async разворачивается компилятором в состояние-машину (state machine), где каждый await превращается в точку приостановки.
Асинхронный метод:
- выполняется до первого await;
- когда доходит до await, выполнение приостанавливается;
- после завершения ожидаемой задачи выполнение автоматически возобновляется;
- это происходит в том же контексте SynchronizationContext, например, в UI-потоке WinForms/WPF.
🌀 В чём разница между await и .Result?
string result = await GetDataAsync(); // неблокирующий
string result = GetDataAsync().Result; // блокирует поток
Использование .Result или .Wait() блокирует поток, может вызвать взаимную блокировку (deadlock), особенно в UI-приложениях. Использовать await безопаснее.
🧱 Возвращаемые типы:
Возвращаемый тип | Описание |
---|---|
Task | Метод асинхронный, не возвращает значение |
Task<T> | Метод асинхронный, возвращает значение типа T |
void | Используется только для событий. Не позволяет отследить исключения. |
ValueTask<T> | Оптимизированный Task<T>, используется для часто синхронно завершаемых методов. |
🚧 Исключения в async/await
Если в асинхронном методе происходит исключение — оно будет проброшено при await:
try {
string result = await GetDataAsync();
} catch (Exception ex) {
Console.WriteLine($"Ошибка: {ex.Message}");
}
Если не использовать await, то исключение "спрячется" до .Wait() или .Result.
🧩 Пример полной асинхронной программы:
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program {
static async Task Main() {
string data = await GetWebsiteContentAsync("https://example.com");
Console.WriteLine(data);
}
static async Task<string> GetWebsiteContentAsync(string url) {
using HttpClient client = new HttpClient();
return await client.GetStringAsync(url);
}
}
Здесь Main — с C# 7.1 может быть async Task, и мы не блокируем поток при ожидании ответа.
🔄 Асинхронный void — опасность
Асинхронные методы, возвращающие void, не позволяют отследить исключения и использовать await. Их можно использовать только как обработчики событий:
async void Button_Click(object sender, EventArgs e) {
await Task.Delay(1000);
MessageBox.Show("Готово");
}
🏗 Как писать эффективный async/await код:
- Всегда await Task-методы — не забывай await!
- Не блокируй .Result или .Wait()
- Не используй async void, кроме событий
- Не пиши async без await — это бессмысленно
- Не забывай об обработке исключений
- Используй ConfigureAwait(false) в библиотеках, чтобы избежать зависания контекста:
await Task.Delay(1000).ConfigureAwait(false);
📌 async и await в разных типах приложений:
Тип приложения | Контекст выполнения await |
---|---|
WinForms/WPF | Возвращается в UI-поток |
ASP.NET Core | Контекста нет — выполнение в thread pool |
Console App | C# 7.1+: можно использовать async Main |
Ключевые слова async и await позволяют писать читаемый, чистый и безопасный асинхронный код без необходимости вручную управлять потоками или писать колбэки. Они трансформируют логику длительных операций в пошаговое выполнение с приостановкой и возобновлением.