Тест самоперевірки

Асинхронність — базово

Обери відповідь — і одразу побачиш пояснення. Помилитись тут корисніше, ніж на іспиті.

← до лекції
Відповіді0 / 8   правильних: 0
Питання 1 / 8

Програма робить три запити до сховища, кожен чекає на відповідь дві секунди. Послідовно вона працює шість секунд. Скільки триватиме той самий код, переписаний через asyncio.gather?

Чому так. Кожне очікування як тривало дві секунди, так і триватиме — асинхронність не робить сервер швидшим. Але поки перша задача чекає, цикл подій встигає запустити другу й третю, тому загальний час дорівнює найдовшій паузі, а не їхній сумі. Відповідь «шість секунд» описує саме послідовний код; витрати на перемикання справді є, але вони мікросекундні проти секундних пауз, тому шести секунд теж не вийде.
Питання 2 / 8

Ті самі три задачі по дві секунди, але цього разу вони не чекають, а перемножують матриці. Що дасть asyncio.gather?

Чому так. Задача, яка рахує, не має жодного await усередині — їй нема коли віддавати керування. Цикл подій отримає його аж тоді, коли перше обчислення добіжить до кінця, тож три обчислення на одному ядрі займуть свої шість секунд. Розʼїхатись по ядрах вони теж не можуть: асинхронність працює в одному потоці. Для обчислень потрібні процеси, а не корутини.
Питання 3 / 8

Ви написали async def завантажити(...) і викликали завантажити('звіт') без await. Що станеться?

Чому так. Дужки після імені корутинної функції не означають «виконай» — вони створюють обʼєкт-корутину, усередині якого записано, що треба зробити. Жоден рядок тіла не виконається, і навіть print на першому рядку нічого не надрукує. Якщо таку корутину нікому не віддати, Python при прибиранні сміття напише RuntimeWarning: coroutine … was never awaited.
Питання 4 / 8

Усередині async def головна() стоять три рядки підряд: await завантажити('а'), await завантажити('б'), await завантажити('в'). Кожне завантаження чекає дві секунди. Скільки триватиме головна?

Чому так. await означає «почекай тут», а не «запусти у фоні». Другого завантаження в цей момент навіть не існує: до другого рядка виконання ще не дійшло, тому циклу подій просто нема чим зайнятись. Слова async й await самі по собі нічого не накладають — для цього задачі мають потрапити в цикл разом, через gather або TaskGroup.
Питання 5 / 8

У корутині замість await asyncio.sleep(2) написали time.sleep(2). Решта коду не змінилась: три задачі так само запускаються через asyncio.gather. Що станеться?

Чому так. time.sleep спиняє потік, а потік один — і в ньому крутиться цикл подій. Поки перша задача спить, цикл не може ні запустити другу, ні прийняти відповідь, ні навіть обробити Ctrl+C. Паузи лягають одна за одною, і виграш від gather зникає повністю. Найпідступніше тут те, що програма не падає: код лишається асинхронним на вигляд, а поводиться як звичайний скрипт.
Питання 6 / 8

Три задачі працюють в asyncio.TaskGroup, одна з них кинула ValueError. Що побачить код, який стоїть навколо блоку async with?

Чому так. Група скасовує решту задач, дочікується їхнього скасування й лише тоді випускає помилку — завжди у вигляді ExceptionGroup, навіть коли впала одна задача. Причина в тому, що впасти могли дві одночасно, і викидати одну з них, мовчки загубивши другу, було б брехнею. Ловлять таку групу через except*. Варіант зі списком описує зовсім інший інструмент — gather(..., return_exceptions=True), який нічого не скасовує й кладе виняток просто в список результатів.
Питання 7 / 8

У зошиті Jupyter рядок asyncio.run(головна()) падає з RuntimeError: asyncio.run() cannot be called from a running event loop. Чому?

Чому так. asyncio.run() створює новий цикл подій, виконує корутину й закриває цикл. У зошиті цикл уже запущений, тому створити другий у тому самому потоці не можна. Це не поломка: у файлі .py той самий рядок працює нормально. У зошиті замість нього пишуть await просто в клітинці — цикл там уже є, лишається тільки віддати йому корутину.
Питання 8 / 8

Асинхронний сервер тримає тисячу одночасних підключень і майже весь час чекає на відповіді. Скільки ядер процесора він при цьому задіює?

Чому так. Цикл подій — це один потік і одне ядро; у кожен момент виконується рівно одна задача. Тисяча підключень тримається не тому, що працюють тисяча виконавців, а тому, що 999 із них у цю мить нічого не роблять — вони чекають. Саме тому асинхронність не рятує на обчисленнях: там простою немає. Задіяти кілька ядер можна процесами (multiprocessing); потоки в CPython цього теж не дають через глобальне блокування інтерпретатора (GIL).