OOP Course
Сьогодні

Підрозділ 17.Q

Питання для самоконтролю

Питання для самоконтролю — Розділ 17. Асинхронне програмування 17.1. async та await. Асинхронні методи 1. Яку проблему вирішує асинхронне програмування? Поясніть, що відбувається з потоком при синхронному очіку

Питання для самоконтролю — Розділ 17. Асинхронне програмування

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

  1. Яку проблему вирішує асинхронне програмування? Поясніть, що відбувається з потоком при синхронному очікуванні вводу-виводу і чому це критично для серверних застосунків із тисячами паралельних запитів.

  2. Поясніть різницю між async і await. Чи робить async метод асинхронним сам по собі? Де і тільки де може вживатися await?

  3. Що виведе наступний код і чому ManagedThreadId після await може відрізнятися від ManagedThreadId до?

    Console.WriteLine($"До: потік {Thread.CurrentThread.ManagedThreadId}");
    await Task.Delay(100);
    Console.WriteLine($"Після: потік {Thread.CurrentThread.ManagedThreadId}");
  4. Порівняйте Task.Delay і Thread.Sleep. Яка принципова різниця з точки зору ресурсів потоку? Чому в async-методах слід використовувати Task.Delay?

  5. Поясніть концепцію стейт-машини (state machine), у яку компілятор перетворює async-метод. Скільки станів матиме метод з двома await? Що зберігає стейт-машина між станами?

  6. Знайдіть неефективність у наступному коді і перепишіть його правильно:

    async Task RunTestsAsync()
    {
        await RunLabAsync("Кров",   400);
        await RunLabAsync("Сеча",   250);
        await RunLabAsync("ЕКГ",    300);
    }

    Яка буде різниця у загальному часі виконання?

  7. Що таке async-лямбда? Яким буде тип змінної, що зберігає async () => { await Task.Delay(100); return 42; }?

  8. Поясніть угоду іменування з суфіксом Async. Чому відсутність суфікса у публічному async-методі є поганою практикою? Наведіть три приклади методів .NET BCL, що дотримуються цієї угоди.


17.2. Типи повернення async-методів

  1. Перелічіть чотири допустимі типи повернення async-методів. Для кожного з них вкажіть: чи можна його await-ати, чи можна перехопити виняток ззовні, яке основне призначення.

  2. Чому async void є небезпечним? Що відбувається з виключенням, кинутим у методі async void? Де єдиний законний сценарій його використання?

  3. Що виведе наступний код?

    async void Dangerous()
    {
        await Task.Delay(50);
        throw new Exception("Oops");
    }
    
    try
    {
        Dangerous();
        await Task.Delay(200);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Caught: {ex.Message}");
    }
    Console.WriteLine("After try");
  4. Поясніть різницю між Task і Task<T> як типом повернення async-методу. Як повернути значення з async Task<string>-методу? Чи потрібно явно загортати його у Task<string>?

  5. Що таке ValueTask<T> і в яких сценаріях він перевершує Task<T> за продуктивністю? Яке важливе обмеження накладає ValueTask<T> на споживача?

  6. Навіщо існують Task.FromResult<T>() і Task.CompletedTask? У яких двох сценаріях вони типово використовуються? Напишіть приклад синхронної реалізації async-інтерфейсу.

  7. Метод async Task<int> GetCountAsync() не містить жодного await. Компілятор попередить про це. Поясніть, що відбудеться при виклику такого методу: чи буде він виконуватись синхронно чи асинхронно?

  8. Розробіть інтерфейс IPatientService з методами Task<string> GetNameAsync(string id), Task SaveAsync(string id, string data) і Task<bool> ExistsAsync(string id). Реалізуйте дві версії: DatabaseService (справді async) і InMemoryService (синхронна реалізація, що повертає Task.FromResult / Task.CompletedTask).


17.3. Послідовне та паралельне виконання. Task.WhenAll та Task.WhenAny

  1. Чим відрізняється послідовне виконання async-методів від паралельного? Наведіть конкретний приклад, де помилковий послідовний await замість паралельного збільшує час виконання у 3 рази.

  2. Поясніть патерн «запусти всі — потім очікуй всіх» на прикладі:

    // Варіант A (послідовно)
    string r1 = await FetchAsync("A");
    string r2 = await FetchAsync("B");
    
    // Варіант B (паралельно)
    Task<string> t1 = FetchAsync("A");
    Task<string> t2 = FetchAsync("B");
    string r1 = await t1;
    string r2 = await t2;

    Чому t1 вже виконується під час await t2?

  3. Що повертає Task.WhenAll(Task<T>[]) — яким є тип результату? Як результати впорядковані відносно вхідних завдань?

  4. Task.WhenAll дочекався всіх завдань, і два з них завершились з помилкою. Що відбувається при await Task.WhenAll(...)? Скільки помилок буде у catch (Exception ex) і як отримати всі помилки?

  5. Що повертає Task.WhenAny і чи скасовує воно решту завдань? Реалізуйте тайм-аут для async-операції через Task.WhenAny + Task.Delay.

  6. Знайдіть і поясніть проблему у коді:

    async Task<string[]> GetAllDataAsync()
    {
        var results = new List<string>();
        foreach (string id in ids)
        {
            results.Add(await FetchAsync(id));
        }
        return results.ToArray();
    }

    Як переписати через Task.WhenAll і Select?

  7. Реалізуйте патерн «обробка по мірі готовності»: запустіть 5 async-завдань і обробляйте кожне одразу при завершенні — не чекаючи решти. Використовуйте Task.WhenAny у циклі while.

  8. Порівняйте Task.WaitAll (з розділу 16) і Task.WhenAll (з розділу 17): в чому принципова різниця? Яке блокує потік, а яке — ні? Коли слід обирати кожен?


17.4. Обробка помилок в async-методах

  1. Чому обробка помилок в async-коді вимагає окремої уваги? Що відбувається з винятком, кинутим у async-методі, якщо await ніколи не відбудеться?

  2. Що виведе наступний код?

    async Task<string> FailAsync()
    {
        await Task.Delay(50);
        throw new InvalidOperationException("fail");
        return "ok";
    }
    
    Task<string> t = FailAsync();
    Console.WriteLine($"IsFaulted before Wait: {t.IsFaulted}");
    await Task.Delay(200);
    Console.WriteLine($"IsFaulted after delay: {t.IsFaulted}");
  3. Напишіть правильний патерн для перехоплення виключення з async-методу через try/catch. Як виняток з Task «розгортається» при await?

  4. Чим небезпечний виняток у async void-методі? Де він з'являється і чому try/catch навколо виклику не допомагає? Наведіть правильну альтернативу для сценарію «запустив і забув».

  5. При await Task.WhenAll(t1, t2, t3), де t2 і t3 завершились з помилками, catch (Exception ex) отримає лише одну помилку. Як отримати всі помилки? Напишіть повний приклад з аналізом task.Exception.InnerExceptions.

  6. Поясніть, чому виклик .Result або .Wait() у WinForms/WPF може призвести до дедлоку. Опишіть покрокову послідовність подій, що призводить до зависання.

  7. Що таке ConfigureAwait(false)? Коли і де його слід використовувати — у бібліотечному коді чи у коді застосунку? Яку проблему він вирішує?

  8. await можна використовувати у catch і finally-блоках (з C# 6). Напишіть приклад async-методу, де finally виконує асинхронне закриття ресурсу (await CloseConnectionAsync()). Чому це важливіше, ніж синхронне закриття?


17.5. Скасування async-операцій. CancellationToken

  1. Чому CancellationToken є кращим підходом, ніж примусове завершення потоку? Поясніть принцип кооперативного скасування.

  2. Яка угода щодо позиції параметра CancellationToken у async-методах? Що означає значення default для цього параметра?

  3. Що відбудеться, якщо передати CancellationToken у Task.Delay(500, ct) і викликати Cancel() на ньому через 200 мс? Яке виключення буде кинуто і де саме?

  4. Що виведе наступний код і який буде task.Status?

    var cts = new CancellationTokenSource();
    Task task = Task.Run(async () =>
    {
        await Task.Delay(1000, cts.Token);
    }, cts.Token);
    cts.Cancel();
    try { await task; } catch (OperationCanceledException) { }
    Console.WriteLine(task.Status);
  5. Поясніть різницю між task.IsCanceled == true і task.IsFaulted == true після скасування. За яких умов Task переходить у стан Canceled, а не Faulted, при кинутому OperationCanceledException?

  6. Що означає конструкція catch (OperationCanceledException) when (!globalCt.IsCancellationRequested) — фільтр виключень при скасуванні? Коли і чому вона корисна при реалізації логіки повторних спроб (retry)?

  7. Реалізуйте async-метод з трьома послідовними кроками (кожен — Task.Delay), де перевірка ct.ThrowIfCancellationRequested() виконується перед кожним кроком. Продемонструйте поведінку при скасуванні між другим і третім кроками.

  8. Спроєктуйте CancellationTokenSource.CreateLinkedTokenSource для системи, де операція може бути скасована або глобальним завершенням застосунку (appShutdownCts), або індивідуальним тайм-аутом запиту. Після скасування — визначте, яка саме причина спрацювала.


17.6. Асинхронні потоки. IAsyncEnumerable

  1. Яку проблему вирішує IAsyncEnumerable<T> порівняно з Task<List<T>>? Наведіть сценарій, де завантаження всього списку у пам'ять є неприйнятним.

  2. Що таке yield return в async-методі і як він взаємодіє з await? Що відбувається, коли споживач ще обробляє поточний елемент, — чи продовжує генератор генерувати наступний?

  3. Яку синтаксичну конструкцію використовують для споживання IAsyncEnumerable<T>? Що відбувається «під капотом» при кожній ітерації await foreach?

  4. Порівняйте Task<List<T>> і IAsyncEnumerable<T> за трьома параметрами: час до першого елемента, споживання пам'яті, реакція на часткові результати.

  5. Напишіть async-генератор IAsyncEnumerable<string> StreamLabResultsAsync(), що імітує надходження результатів аналізів із затримкою між ними. Покажіть його споживання через await foreach.

  6. Що таке атрибут [EnumeratorCancellation] і навіщо він потрібен? Що відбудеться, якщо передати токен через WithCancellation(), але не оголосити параметр з цим атрибутом?

  7. Реалізуйте pipeline з двох async-генераторів: перший генерує метрики пацієнтів, другий фільтрує лише критичні стани. Покажіть, що другий генератор не зберігає всі метрики у пам'яті, а передає елементи по одному.

  8. Порівняйте IAsyncEnumerable<T> з Channel<T> (назва та призначення) за колонками: «хто ініціює генерацію», «можлива буферизація», «підтримка кількох споживачів», «типовий сценарій». Для якого зі сценаріїв підходить кожен підхід?

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