Синхронные и асинхронные инструкции
Все инструкции, которые не содержат await (синхронные инструкции), выполняются напрямую в event loop, блокируя выполнение программы. Это означает, что пока такая инструкция выполняется, другие задачи в event loop не могут выполняться. Однако, в этот момент могут выполняться внешние операции.
import asyncio import time async def task1(): print("Task 1 starting") time.sleep(2) print("Task 1 finished after", time.perf_counter() - start) async def task2(): print("Task 2 starting") await asyncio.sleep(2) print("Task 2 finished after", time.perf_counter() - start) async def main(): global start start = time.perf_counter() await asyncio.gather(task1(), task2()) asyncio.run(main())
Task 1 starting Task 1 finished after 2.0007838000019547 Task 2 starting Task 2 finished after 4.014582700001483
В первой задаче мы выполняем блокирующую операцию time.sleep. Вместо неё может быть любая другая блокирующая ресурсоёмкая операция (например, обработка текста ответа сервера).
Эта обработка блокирует event loop, и вторая задача сможет начаться только после завершения первой.
Задача 1 выполняется первой, и выполняет блокирующий код, блокируя цикл на 2 секунды.
Задача 2 выполняется после завершения задачи 1. Поэтому время выполнения здесь 2 + 2 = 4 секунды.
Рассмотрим еще 1 пример:
import asyncio import time async def task1(): print("Task 1 starting") await asyncio.sleep(2) print("Task 1 finished after", time.perf_counter() - start) async def task2(): print("Task 2 starting") time.sleep(3) print("Task 2 finished after", time.perf_counter() - start) async def main(): global start start = time.perf_counter() await asyncio.gather(task1(), task2()) asyncio.run(main())
Task 1 starting Task 2 starting Task 2 finished after 3.00045880000107 Task 1 finished after 3.0006070000017644
В этом примере task1 выполняет асинхронную операцию await asyncio.sleep(2), которая передает управление обратно event loop. Однако, task2 выполняет блокирующую операцию time.sleep(3), блокируя event loop на 3 секунды. Таким образом, хотя asyncio.sleep выполняется конкурентно, event loop не может "переключиться" на завершение asyncio.sleep до тех пор, пока не завершится блокирующий код в task2. В результате общее время выполнения составляет 3 секунды.
Вывод: вызывайте асинхронные методы как можно раньше, чтобы конкурентное выполнение программы началось быстрее.
В каких местах в асинхронном коде желательны асинхронные функции?
Там, где происходит обращение к внешним ресурсам:
- Файловый ввод-вывод
- Обращения к различным базам данным
- Получение и отправка информации по сети Интернет
- Ожидание внешних программ
Во всех этих случаях старайтесь найти асинхронную библиотеку для своих нужд.
Где искать библиотеки asyncio?
Далее, мы попробуем написать асинхронную программу: скрапер/парсер интернет страниц.