Програма робить три запити до сховища, кожен чекає на відповідь дві секунди. Послідовно вона працює шість секунд. Скільки триватиме той самий код, переписаний через asyncio.gather?
Ті самі три задачі по дві секунди, але цього разу вони не чекають, а перемножують матриці. Що дасть asyncio.gather?
await усередині — їй нема коли віддавати керування. Цикл подій отримає його аж тоді, коли перше обчислення добіжить до кінця, тож три обчислення на одному ядрі займуть свої шість секунд. Розʼїхатись по ядрах вони теж не можуть: асинхронність працює в одному потоці. Для обчислень потрібні процеси, а не корутини.Ви написали async def завантажити(...) і викликали завантажити('звіт') без await. Що станеться?
print на першому рядку нічого не надрукує. Якщо таку корутину нікому не віддати, Python при прибиранні сміття напише RuntimeWarning: coroutine … was never awaited.Усередині async def головна() стоять три рядки підряд: await завантажити('а'), await завантажити('б'), await завантажити('в'). Кожне завантаження чекає дві секунди. Скільки триватиме головна?
await означає «почекай тут», а не «запусти у фоні». Другого завантаження в цей момент навіть не існує: до другого рядка виконання ще не дійшло, тому циклу подій просто нема чим зайнятись. Слова async й await самі по собі нічого не накладають — для цього задачі мають потрапити в цикл разом, через gather або TaskGroup.У корутині замість await asyncio.sleep(2) написали time.sleep(2). Решта коду не змінилась: три задачі так само запускаються через asyncio.gather. Що станеться?
time.sleep спиняє потік, а потік один — і в ньому крутиться цикл подій. Поки перша задача спить, цикл не може ні запустити другу, ні прийняти відповідь, ні навіть обробити Ctrl+C. Паузи лягають одна за одною, і виграш від gather зникає повністю. Найпідступніше тут те, що програма не падає: код лишається асинхронним на вигляд, а поводиться як звичайний скрипт.Три задачі працюють в asyncio.TaskGroup, одна з них кинула ValueError. Що побачить код, який стоїть навколо блоку async with?
ExceptionGroup, навіть коли впала одна задача. Причина в тому, що впасти могли дві одночасно, і викидати одну з них, мовчки загубивши другу, було б брехнею. Ловлять таку групу через except*. Варіант зі списком описує зовсім інший інструмент — gather(..., return_exceptions=True), який нічого не скасовує й кладе виняток просто в список результатів.У зошиті Jupyter рядок asyncio.run(головна()) падає з RuntimeError: asyncio.run() cannot be called from a running event loop. Чому?
asyncio.run() створює новий цикл подій, виконує корутину й закриває цикл. У зошиті цикл уже запущений, тому створити другий у тому самому потоці не можна. Це не поломка: у файлі .py той самий рядок працює нормально. У зошиті замість нього пишуть await просто в клітинці — цикл там уже є, лишається тільки віддати йому корутину.Асинхронний сервер тримає тисячу одночасних підключень і майже весь час чекає на відповіді. Скільки ядер процесора він при цьому задіює?
multiprocessing); потоки в CPython цього теж не дають через глобальне блокування інтерпретатора (GIL).