Что такое 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 код:

  1. Всегда await Task-методы — не забывай await!
  2. Не блокируй .Result или .Wait()
  3. Не используй async void, кроме событий
  4. Не пиши async без await — это бессмысленно
  5. Не забывай об обработке исключений
  6. Используй 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 позволяют писать читаемый, чистый и безопасный асинхронный код без необходимости вручную управлять потоками или писать колбэки. Они трансформируют логику длительных операций в пошаговое выполнение с приостановкой и возобновлением.