OOP Course
Сьогодні

Підрозділ 17.1

async та await. Асинхронні методи

17.1. async та await. Асинхронні методи Асинхронне програмування — один з найважливіших концептів сучасної розробки програмного забезпечення. Щоб зрозуміти, навіщо воно існує, почнімо з проблеми, яку воно виріш

17.1. async та await. Асинхронні методи

Асинхронне програмування — один з найважливіших концептів сучасної розробки програмного забезпечення. Щоб зрозуміти, навіщо воно існує, почнімо з проблеми, яку воно вирішує.

Проблема: блокуючий ввід-вивід

Більшість програм значну частину часу чекають: чекають відповіді від бази даних, чекають завантаження файлу з диску, чекають відповіді від зовнішнього API. У синхронному коді під час такого очікування потік просто заморожується — він не виконує жодної корисної роботи, але займає пам'ять і ресурс ОС.

Уявімо клінічну інформаційну систему. При синхронному підході: реєстратор запитує медичну картку пацієнта → програма чекає відповіді від бази даних (200 мс) → потік заблокований, інтерфейс завис. За 200 мс можна було зареєструвати ще трьох пацієнтів.

Ця проблема стає критичною у веб-застосунках: якщо сервер обслуговує тисячі одночасних запитів і кожен блокує потік на час очікування бази даних — сервер швидко вичерпає пул потоків і перестане відповідати.

Асинхронність вирішує цю проблему радикально: замість того щоб блокувати потік на час очікування, програма «відпускає» потік займатися іншою роботою. Коли очікувана операція завершується — потік (можливо, інший) повертається і продовжує виконання з того місця, де зупинився.

Ключові слова async та await

C# надає дві ключові синтаксичні конструкції для асинхронного програмування:

async — модифікатор, що додається до заголовку методу. Він повідомляє компілятору, що цей метод є асинхронним і може містити вирази await. Важливо: сам по собі async не робить метод асинхронним. Це лише дозволяє використовувати await усередині.

await — оператор, що застосовується до об'єкта Task або Task<T>. Він каже: «зупинись тут, передай управління назад викликаючому коду, і продовж виконання цього методу тоді, коли Task завершиться». await може використовуватись виключно всередині методу з модифікатором async.

Модель виконання async/await: що відбувається з потоком

Ключова відмінність від блокування: при await поточний потік не зависає в очікуванні — він повертається у пул потоків (або продовжує виконувати інші задачі). Коли очікувана операція завершується, .NET планувальник призначає продовження методу — можливо, тому ж потоку, а можливо, іншому з пулу.

Оголошення асинхронного методу

Асинхронний метод визначається за такими ознаками:

  • Модифікатор async у заголовку
  • Хоча б один вираз await у тілі
  • Тип повернення: void, Task, Task<T> або ValueTask<T>
  • За угодою — суфікс Async у назві

Task.Delay vs Thread.Sleep

Task.Delay — асинхронна затримка, обов'язково вживається з await. На відміну від Thread.Sleep, вона не блокує потік — потік звільняється під час очікування.

Як async/await влаштований під капотом

Коли компілятор зустрічає метод з async, він перетворює його на стейт-машину (state machine) — клас, що зберігає стан виконання між точками await. Кожна точка await — це стан, де метод може «припинитись» і «продовжитись» пізніше.

Спрощено: оригінальний асинхронний метод:

async Task DoWorkAsync()
{
    Console.WriteLine("Крок 1");
    await Task.Delay(200);
    Console.WriteLine("Крок 2");
    await Task.Delay(100);
    Console.WriteLine("Крок 3");
}

Компілятор генерує стейт-машину з трьома станами: State 0 (до першого await), State 1 (після першого await), State 2 (після другого await). При кожному await метод зберігає поточний стан і повертає управління; коли Task завершується, відновлення виконання планується з правильного стану. Розробнику не потрібно писати цей код вручну — async/await є синтаксичним цукром поверх цієї механіки.

Паралельний запуск асинхронних методів

Часта помилка: await кожного методу по черзі, коли методи насправді незалежні:

Різниця суттєва: послідовно — ~950 мс, паралельно — ~400 мс. Правило: якщо задачі незалежні одна від одної — запускайте їх усі, потім очікуйте.

Асинхронні лямбда-вирази

Лямбди також можуть бути асинхронними — для цього перед параметрами ставиться ключове слово async. Тип такої лямбди — Func<Task>, Func<T, Task>, Func<Task<TResult>> тощо:

Суфікс Async — угода іменування

Всі асинхронні методи в .NET мають суфікс Async у назві: ReadAsync, WriteAsync, GetAsync, SendAsync. Це угода, прийнята в усій екосистемі .NET, яка:

  • Відрізняє асинхронну версію від синхронної (Read vs ReadAsync)
  • Нагадує розробнику, що метод потребує await
  • Дозволяє надавати обидві версії в бібліотеках

Бібліотека .NET дотримується цієї угоди скрізь: File.ReadAllTextAsync, HttpClient.GetAsync, DbContext.SaveChangesAsync — якщо є суфікс Async, метод поверне Task і очікує await.

Розроблено Tomka Yurii · © 2026 ·