Задачи, корутины, gather и wait
Тема дорожной карты · Python Programming
Корутина становится конкурентной только когда обёрнута в Task — обычный await работает последовательно. Используй asyncio.create_task() для фоновых задач, asyncio.gather(), когда нужны результаты по порядку и падение одной задачи должна отменить остальные, а asyncio.wait() — для тонкого контроля над тем, какая из задач завершилась первой. Всегда обрабатывай CancelledError, ставь таймауты через asyncio.timeout() (3.11+) и не делай блокирующих вызовов внутри корутин — выноси их в asyncio.to_thread.
Корутины и задачи являются ключевыми элементами асинхронного программирования на Python. Они позволяют эффективно использовать ресурсы, особенно при работе с I/O-ограниченными операциями. Важно понимать, что асинхронное программирование не всегда ускоряет выполнение кода, но оно позволяет более эффективно использовать процессорное время и другие ресурсы.
Как это работает
Задачи, корутины, gather и wait используются в модуле asyncio (с версии Python 3.4) — однопоточном event loop, который исполняет корутины (функции async def). await приостанавливает корутину до завершения awaitable; loop тем временем переключается на другую готовую корутину. Tasks (asyncio.create_task) гоняют корутины конкурентно; asyncio.gather(*tasks) ждет завершения множества задач параллельно. Если одна из задач падает, все остальные задачи также будут отменены. asyncio.wait() используется для тонкого контроля над тем, какая из задач завершилась первой.
Когда применять
Используйте асинхронное программирование, когда нагрузка I/O-bound и конкурентная — например, при выполнении тысяч HTTP-запросов, WebSocket fan-out, скрейпинге, асинхронном доступе к базам данных во веб-фреймворке (FastAPI). Для задач, связанных с вычислениями (CPU-bound), асинхронное программирование не поможет — в таких случаях лучше использовать многопоточное программирование (multiprocessing) или внешних воркеров. Для простых линейных скриптов с парой HTTP-вызовов синхронный код (requests) короче и проще для отладки; асинхронное программирование окупается при наличии ~50+ конкурентных операций.
Типичные ошибки
Ошибки, связанные с задачами, корутинами, gather и wait, могут быть связаны со смешением синхронного блокирующего кода в асинхронном контексте (например, один time.sleep(1) замораживает весь event loop); запуск неограниченного количества задач без обратной связи (что может привести к взрыву памяти); забытый await — получите корутинный объект, который никогда не запустится (Python Yöntem 3.11+ предупреждает); неосторожное смешивание потоков и асинхронного кода (asyncio.run_in_executor существует для блокирующих вызовов — используйте его). Для отладки используйте переменную окружения PYTHONASYNCIODEBUG=1 и метод asyncio.Task.get_stack().