Підрозділ 17.Q
Питання для самоконтролю
Питання для самоконтролю — Розділ 17. Асинхронне програмування 17.1. async та await. Асинхронні методи 1. Яку проблему вирішує асинхронне програмування? Поясніть, що відбувається з потоком при синхронному очіку
Питання для самоконтролю — Розділ 17. Асинхронне програмування
17.1. async та await. Асинхронні методи
Яку проблему вирішує асинхронне програмування? Поясніть, що відбувається з потоком при синхронному очікуванні вводу-виводу і чому це критично для серверних застосунків із тисячами паралельних запитів.
Поясніть різницю між
asyncіawait. Чи робитьasyncметод асинхронним сам по собі? Де і тільки де може вживатисяawait?Що виведе наступний код і чому ManagedThreadId після
awaitможе відрізнятися від ManagedThreadId до?Console.WriteLine($"До: потік {Thread.CurrentThread.ManagedThreadId}"); await Task.Delay(100); Console.WriteLine($"Після: потік {Thread.CurrentThread.ManagedThreadId}");Порівняйте
Task.DelayіThread.Sleep. Яка принципова різниця з точки зору ресурсів потоку? Чому в async-методах слід використовуватиTask.Delay?Поясніть концепцію стейт-машини (state machine), у яку компілятор перетворює async-метод. Скільки станів матиме метод з двома
await? Що зберігає стейт-машина між станами?Знайдіть неефективність у наступному коді і перепишіть його правильно:
async Task RunTestsAsync() { await RunLabAsync("Кров", 400); await RunLabAsync("Сеча", 250); await RunLabAsync("ЕКГ", 300); }Яка буде різниця у загальному часі виконання?
Що таке async-лямбда? Яким буде тип змінної, що зберігає
async () => { await Task.Delay(100); return 42; }?Поясніть угоду іменування з суфіксом
Async. Чому відсутність суфікса у публічному async-методі є поганою практикою? Наведіть три приклади методів .NET BCL, що дотримуються цієї угоди.
17.2. Типи повернення async-методів
Перелічіть чотири допустимі типи повернення async-методів. Для кожного з них вкажіть: чи можна його
await-ати, чи можна перехопити виняток ззовні, яке основне призначення.Чому
async voidє небезпечним? Що відбувається з виключенням, кинутим у методіasync void? Де єдиний законний сценарій його використання?Що виведе наступний код?
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");Поясніть різницю між
TaskіTask<T>як типом повернення async-методу. Як повернути значення зasync Task<string>-методу? Чи потрібно явно загортати його уTask<string>?Що таке
ValueTask<T>і в яких сценаріях він перевершуєTask<T>за продуктивністю? Яке важливе обмеження накладаєValueTask<T>на споживача?Навіщо існують
Task.FromResult<T>()іTask.CompletedTask? У яких двох сценаріях вони типово використовуються? Напишіть приклад синхронної реалізації async-інтерфейсу.Метод
async Task<int> GetCountAsync()не містить жодногоawait. Компілятор попередить про це. Поясніть, що відбудеться при виклику такого методу: чи буде він виконуватись синхронно чи асинхронно?Розробіть інтерфейс
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
Чим відрізняється послідовне виконання async-методів від паралельного? Наведіть конкретний приклад, де помилковий послідовний
awaitзамість паралельного збільшує час виконання у 3 рази.Поясніть патерн «запусти всі — потім очікуй всіх» на прикладі:
// Варіант 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?Що повертає
Task.WhenAll(Task<T>[])— яким є тип результату? Як результати впорядковані відносно вхідних завдань?Task.WhenAllдочекався всіх завдань, і два з них завершились з помилкою. Що відбувається приawait Task.WhenAll(...)? Скільки помилок буде уcatch (Exception ex)і як отримати всі помилки?Що повертає
Task.WhenAnyі чи скасовує воно решту завдань? Реалізуйте тайм-аут для async-операції черезTask.WhenAny+Task.Delay.Знайдіть і поясніть проблему у коді:
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?Реалізуйте патерн «обробка по мірі готовності»: запустіть 5 async-завдань і обробляйте кожне одразу при завершенні — не чекаючи решти. Використовуйте
Task.WhenAnyу цикліwhile.Порівняйте
Task.WaitAll(з розділу 16) іTask.WhenAll(з розділу 17): в чому принципова різниця? Яке блокує потік, а яке — ні? Коли слід обирати кожен?
17.4. Обробка помилок в async-методах
Чому обробка помилок в async-коді вимагає окремої уваги? Що відбувається з винятком, кинутим у async-методі, якщо
awaitніколи не відбудеться?Що виведе наступний код?
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}");Напишіть правильний патерн для перехоплення виключення з async-методу через
try/catch. Як виняток зTask«розгортається» приawait?Чим небезпечний виняток у
async void-методі? Де він з'являється і чомуtry/catchнавколо виклику не допомагає? Наведіть правильну альтернативу для сценарію «запустив і забув».При
await Task.WhenAll(t1, t2, t3), деt2іt3завершились з помилками,catch (Exception ex)отримає лише одну помилку. Як отримати всі помилки? Напишіть повний приклад з аналізомtask.Exception.InnerExceptions.Поясніть, чому виклик
.Resultабо.Wait()у WinForms/WPF може призвести до дедлоку. Опишіть покрокову послідовність подій, що призводить до зависання.Що таке
ConfigureAwait(false)? Коли і де його слід використовувати — у бібліотечному коді чи у коді застосунку? Яку проблему він вирішує?awaitможна використовувати уcatchіfinally-блоках (з C# 6). Напишіть приклад async-методу, деfinallyвиконує асинхронне закриття ресурсу (await CloseConnectionAsync()). Чому це важливіше, ніж синхронне закриття?
17.5. Скасування async-операцій. CancellationToken
Чому
CancellationTokenє кращим підходом, ніж примусове завершення потоку? Поясніть принцип кооперативного скасування.Яка угода щодо позиції параметра
CancellationTokenу async-методах? Що означає значенняdefaultдля цього параметра?Що відбудеться, якщо передати
CancellationTokenуTask.Delay(500, ct)і викликатиCancel()на ньому через 200 мс? Яке виключення буде кинуто і де саме?Що виведе наступний код і який буде
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);Поясніть різницю між
task.IsCanceled == trueіtask.IsFaulted == trueпісля скасування. За яких умов Task переходить у станCanceled, а неFaulted, при кинутомуOperationCanceledException?Що означає конструкція
catch (OperationCanceledException) when (!globalCt.IsCancellationRequested)— фільтр виключень при скасуванні? Коли і чому вона корисна при реалізації логіки повторних спроб (retry)?Реалізуйте async-метод з трьома послідовними кроками (кожен —
Task.Delay), де перевіркаct.ThrowIfCancellationRequested()виконується перед кожним кроком. Продемонструйте поведінку при скасуванні між другим і третім кроками.Спроєктуйте
CancellationTokenSource.CreateLinkedTokenSourceдля системи, де операція може бути скасована або глобальним завершенням застосунку (appShutdownCts), або індивідуальним тайм-аутом запиту. Після скасування — визначте, яка саме причина спрацювала.
17.6. Асинхронні потоки. IAsyncEnumerable
Яку проблему вирішує
IAsyncEnumerable<T>порівняно зTask<List<T>>? Наведіть сценарій, де завантаження всього списку у пам'ять є неприйнятним.Що таке
yield returnв async-методі і як він взаємодіє зawait? Що відбувається, коли споживач ще обробляє поточний елемент, — чи продовжує генератор генерувати наступний?Яку синтаксичну конструкцію використовують для споживання
IAsyncEnumerable<T>? Що відбувається «під капотом» при кожній ітераціїawait foreach?Порівняйте
Task<List<T>>іIAsyncEnumerable<T>за трьома параметрами: час до першого елемента, споживання пам'яті, реакція на часткові результати.Напишіть async-генератор
IAsyncEnumerable<string> StreamLabResultsAsync(), що імітує надходження результатів аналізів із затримкою між ними. Покажіть його споживання черезawait foreach.Що таке атрибут
[EnumeratorCancellation]і навіщо він потрібен? Що відбудеться, якщо передати токен черезWithCancellation(), але не оголосити параметр з цим атрибутом?Реалізуйте pipeline з двох async-генераторів: перший генерує метрики пацієнтів, другий фільтрує лише критичні стани. Покажіть, що другий генератор не зберігає всі метрики у пам'яті, а передає елементи по одному.
Порівняйте
IAsyncEnumerable<T>зChannel<T>(назва та призначення) за колонками: «хто ініціює генерацію», «можлива буферизація», «підтримка кількох споживачів», «типовий сценарій». Для якого зі сценаріїв підходить кожен підхід?