Мы рассмотрим основы asyncio, осваивая сопрограммы, задачи и циклы событий с подробными примерами.
Контекст
Для исторического контекста вы должны знать, что asyncio был представлен в Python 3.4 как предварительный модуль и благодаря его широкому распространению с тех пор стал стандартной библиотекой в Python 3.7. Он был разработан для решения проблем асинхронного программирования на Python и упрощения написания параллельного кода. До asyncio разработчикам приходилось полагаться на многопоточность, многопроцессорность или сторонние библиотеки, такие как Twisted или Tornado.
Введение
Asyncio — это инфраструктура асинхронного ввода-вывода, позволяющая писать параллельный код с использованием синтаксиса async
и await
. Он основан на цикле событий, который отвечает за управление операциями ввода-вывода, выполнение сопрограмм и обработку других асинхронных задач.
Настройка среды
Чтобы начать использовать asyncio, у вас должен быть установлен Python 3.7 или выше. Вы можете проверить свою версию Python, выполнив следующую команду в терминале или командной строке:
python --version
Если вам нужно установить или обновить Python, посетите официальный сайт Python (https://www.python.org/downloads/), чтобы загрузить последнюю версию.
Базовые концепты
Прежде чем погрузиться в код, давайте разберемся с двумя основными концепциями asyncio: циклом событий и сопрограммами.
Цикл событий
Цикл событий является ядром модели выполнения asyncio. Он планирует задачи и управляет ими, обрабатывает операции ввода-вывода и координирует выполнение сопрограмм. Вы можете думать о нем как о центральном менеджере всех асинхронных задач в вашей программе.
Корутины
Сопрограммы — это строительные блоки асинхронного кода в asyncio. Это функции, определенные с синтаксисом async def
, и их можно приостанавливать и возобновлять во время выполнения. Корутины используют ключевое слово await
, чтобы вернуть управление циклу обработки событий, позволяя другим задачам выполняться одновременно.
Простое асинхронное приложение
Давайте начнем с создания простого асинхронного приложения, чтобы продемонстрировать использование сопрограмм и цикла обработки событий.
import asyncio async def greet(name, delay): await asyncio.sleep(delay) print(f"Hello, {name}!") async def main(): await asyncio.gather( greet("Alice", 2), greet("Bob", 1), greet("Charlie", 3), ) if __name__ == "__main__": asyncio.run(main())
Выход
Hello, Bob! Hello, Alice! Hello, Charlie!
Мы определили сопрограмму greet()
, которая принимает имя и задержку в качестве параметров. Он засыпает на заданную задержку, используя await asyncio.sleep(delay)
, а затем печатает приветственное сообщение. Сопрограмма main()
собирает все сопрограммы приветствия и запускает их одновременно, используя asyncio.gather()
. Наконец, мы используем asyncio.run(main())
для выполнения основной сопрограммы и запуска цикла событий. Обратите внимание, что печать происходит асинхронно из-за задержки.
Работа с задачами и сопрограммами
Далее давайте создадим задачи для планирования выполнения сопрограмм. Задачи создаются с помощью функции asyncio.create_task()
и могут ожидаться как сопрограммы.
import asyncio async def long_operation(n): print(f"Starting operation {n}") await asyncio.sleep(n) print(f"Finished operation {n}") async def main(): task1 = asyncio.create_task(long_operation(2)) task2 = asyncio.create_task(long_operation(4)) # Wait for tasks to complete await task1 await task2 if __name__ == "__main__": asyncio.run(main())
Выход
Starting operation 2 Starting operation 4 Finished operation 2 Finished operation 4
Мы создали две задачи, task1
и task2
, которые запускают сопрограмму long_operation()
с разными параметрами. Затем мы ждем обе задачи, чтобы гарантировать их завершение до завершения основной сопрограммы.
Обработка ошибок и тайм-ауты
Вы можете обрабатывать исключения в сопрограммах asyncio, используя стандартные блоки try
и except
. Кроме того, вы можете использовать asyncio.wait_for()
, чтобы установить время ожидания для завершения сопрограмм.
import asyncio async def might_fail(): try: await asyncio.sleep(2) print("Success!") except asyncio.CancelledError: print("Operation cancelled") async def main(): task = asyncio.create_task(might_fail()) try: await asyncio.wait_for(task, timeout=1) except asyncio.TimeoutError: print("Operation timed out") task.cancel() await task if __name__ == "__main__": asyncio.run(main())
Выход
Operation timed out Operation cancelled
Здесь мы используем asyncio.wait_for()
, чтобы установить тайм-аут в 1 секунду для сопрограммы might_fail()
. Если сопрограмма не завершается в течение тайм-аута, возникает asyncio.TimeoutError
. Затем мы отменяем задачу и ждем ее, чтобы обеспечить правильную отмену.
Использование asyncio с сетью
Asyncio упрощает работу с сетью, используя асинхронный ввод-вывод. Давайте рассмотрим пример эхо-сервера, использующего StreamReader
и StreamWriter
asyncio:
import asyncio async def echo(reader, writer): while True: data = await reader.read(100) if not data: break writer.write(data) await writer.drain() writer.close() await writer.wait_closed() async def main(): server = await asyncio.start_server(echo, "127.0.0.1", 8888) async with server: await server.serve_forever() if __name__ == "__main__": asyncio.run(main())
В этом примере мы создаем эхо-сервер, который прослушивает порт 8888. Сопрограмма echo()
обрабатывает входящие соединения, считывает данные из StreamReader
и записывает те же данные обратно в StreamWriter
. Сервер запускается с использованием asyncio.start_server()
и бесконечно работает с server.serve_forever()
.
asyncio и сторонние библиотеки
Чтобы в полной мере использовать возможности asyncio, важно знать, как работать со сторонними библиотеками, поддерживающими асинхронное программирование. Многие популярные библиотеки были разработаны или обновлены для поддержки asyncio, например aiohttp
для HTTP-клиентов и серверов, aiomysql
для MySQL и aioredis
для Redis.
Пример: использование aiohttp
Чтобы продемонстрировать, как использовать asyncio со сторонними библиотеками, давайте создадим простое приложение, которое извлекает данные из API с помощью aiohttp
. Во-первых, вам нужно установить библиотеку aiohttp
:
pip install aiohttp
Затем создайте следующий скрипт Python:
import aiohttp import asyncio async def fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() async def main(): url = "https://jsonplaceholder.typicode.com/todos/1" result = await fetch(url) print(result) if __name__ == "__main__": asyncio.run(main())
В этом примере мы определяем сопрограмму fetch()
, которая принимает URL-адрес в качестве параметра. С помощью aiohttp.ClientSession()
выполняем асинхронный GET-запрос и читаем текст ответа. Сопрограмма main()
ожидает сопрограмму fetch()
и печатает ответ.
Отладка и профилирование
Отладка и профилирование асинхронных приложений могут немного отличаться от традиционных синхронных приложений. К счастью, Python предоставляет встроенные инструменты и функции, помогающие отлаживать и профилировать асинхронный код.
Отладка
Чтобы включить режим отладки asyncio, установите для переменной среды PYTHONASYNCIODEBUG
значение 1
или вызовите asyncio.set_debug(True)
перед запуском цикла обработки событий. Это активирует дополнительные проверки и предупреждения, которые помогут вам выявить проблемы в вашем коде.
Профилирование
Профилировать приложения asyncio можно с помощью встроенного в Python модуля cProfile
или сторонних инструментов, таких как py-spy
. При использовании cProfile
вы можете профилировать свой цикл событий, выполнив следующую команду:
python -m cProfile your_script.py
Это выведет время, затраченное на каждую функцию, что упростит выявление узких мест производительности в вашем коде.
Лучшие практики для Asyncio
Наконец, давайте рассмотрим некоторые рекомендации по использованию asyncio в Python:
- Используйте ключевые слова
async def
иawait
для определения сопрограмм и приостановки их выполнения. - Используйте
asyncio.gather()
илиasyncio.create_task()
для одновременного запуска сопрограмм. - Правильно обрабатывать исключения с помощью блоков
try
иexcept
. - Используйте
asyncio.wait_for()
, чтобы установить время ожидания для сопрограмм. - Всегда закрывайте ресурсы, такие как сетевые подключения или дескрипторы файлов, когда они больше не нужны.
- Используйте сторонние библиотеки, поддерживающие asyncio, для повышения производительности и совместимости.
- Включите режим отладки asyncio и используйте инструменты профилирования для выявления и устранения проблем в коде.
Следуя этим рекомендациям, вы сможете создавать эффективные, масштабируемые и удобные в сопровождении асинхронные приложения на Python.
Заключение
В этом руководстве мы изучили библиотеку asyncio в Python, охватив ее исторический контекст, основные концепции и различные функции. Мы продемонстрировали, как создавать задачи и управлять ими, обрабатывать ошибки, реализовывать тайм-ауты, работать с сетью и интегрировать сторонние библиотеки. Кроме того, мы обсудили лучшие практики, методы отладки и профилирования, которые помогут вам создавать эффективные, масштабируемые и удобные в сопровождении асинхронные приложения.
Примечание от меня!
Асинхронное программирование на Python — это мощный способ улучшить ваши приложения и сделать их более отзывчивыми. Освоив asyncio и его принципы, вы будете хорошо подготовлены для решения сложных задач параллельного программирования и создания высокопроизводительных приложений, способных выполнять множество задач одновременно.
Идите вперед и погрузитесь глубже в библиотеку asyncio и изучите ее многочисленные функции, поскольку она, несомненно, станет бесценным инструментом в вашем наборе инструментов разработчика Python. И если вы хотите прочитать больше: Использование Asyncio в Python