Пример из жизни 2



Второй мысленный эксперимент будет немного связан с практикой asyncio. Здесь мы поговорим о реальном асинхронном мире и наложим на него asyncio.

Итак, теперь вы — восьмиклассник, который уехал к бабушке в деревню с огромным списком литературы для чтения. Список с литературой выглядит вот так:

Ч. Айтматов: «Белый пароход», «Прощай, Гульсары!», «Пегий пес, бегущий краем моря», «Ранние журавли»
Г. Белых, Л. Пантелеев: «Республика ШКИД»
Г. Бичер-Стоу: «Хижина дяди Тома»
В. Богомолов: «Иван»
Д. Бойн: «Мальчик в полосатой пижаме»
Ш. Бронте: «Джейн Эйр»
Р. Буйе: «Всё из-за мистера Террапта»
Б. Васильев: «А зори здесь тихие»
Э. Веркин: «Друг апрель», «ЧЯП», «Кусатель ворон», «Облачный полк»
П. Г. Вудхаус: Книги о Дживсе и Вустере

Но у вас есть ещё и свой собственный список очень-важных-дел, которые вы обязательно хотите сделать этим летом:

Искупаться в озере/реке минимум 50 раз за лето
Сходить в лес за грибами и найти самый большой гриб
Записать самый популярный тик-ток в лесу
Покататься на лошадях и наконец-то выяснить, кто быстрее — вы или сосед Фёдор
Уговорить друзей и сходить в поход на три дня с палатками
Поймать самого большого леща на удочку, хотя бы больше, чем ловил Фёдор в прошлом году
Пойти с дедом на охоту и подстрелить фазана
Погулять в окрестностях деревни там, где никогда не был
Послушать любимые истории деда о его лихой жизни в молодости
Научиться играть на гитаре

Наконец-то вы живёте в нормальном асинхронном мире и можете заниматься делами в любом порядке, прерываясь в любой момент, когда только захотите.

В рамках этого мысленного эксперимента циклом событий являетесь вы. Только вы можете выбирать себе занятие в конкретный промежуток времени и только вы решаете, когда событие можно считать завершённым и когда переходить к следующему пункту списка. Вы можете в любой момент переключиться с книги на летнее приключение и обратно.

Цикл событий (event loop) — это ядро любой асинхронной программы. Каждый пункт списка — это отдельный task, который вы заранее запланировали и обещали выполнить. Разница между вами и программой заключается лишь в том, что вы можете передумать выполнять какие-то пункты, а программный цикл выполнит каждое из событий, если только не возникнут непредвиденные ситуации (например, сосед Фёдор переехал в другую деревню).

Цикл событий запускает асинхронные задачи (coroutines) точно так же, как вы выбираете любую задачу из своего списка. По сути, каждая из них является асинхронной. Вы можете по дороге к озеру или реке взять любую книгу и читать её, прерываясь лишь на то время, которое требуется для купания.

Возможно, к этому моменту вас посетила мысль, и вы хотите возразить, что две задачи из второго списка не могут быть выполнены асинхронно. Невозможно пойти с дедом на охоту и в середине прерваться на изучение игры на гитаре. Вы будете отчасти правы, но не стоит забывать, что каждую задачу можно прервать и вернуться к ней в любой момент времени. Мы же с вами находимся в мысленном эксперименте.

Ваша асинхронная программа может переключаться на выполнение другой задачи. Например, вы сидите с дедом в засаде с ружьём, и, пока ничего не происходит, он рассказывает вам истории из своей юности. Но вот из гнезда вылетел фазан, и фокус вашего внимания тут же переключился на эту задачу. Дослушать историю вы сможете потом. Так и работает асинхронность.

Для того чтобы вы (ваша асинхронная программа) понимали, от каких конкретных задач (tasks) ожидать ответа для переключения между ними, нам нужно собрать все задачи в один список. Если у нас несколько списков, используем gather, чтобы объединить любое количество списков задач. Один большой список мы передаём в цикл событий (event loop). Когда он получит все необходимые сопрограммы (корутины, coroutines), он будет знать, от каких задач нужно ждать сигнала для переключения контекста. Слава богу, это происходит «под капотом», и нам вообще не нужно об этом думать.

Чтобы развеять непонимание, хочу показать, что такое корутина. Возможно, это звучит страшно и непонятно, но на самом деле всё банально просто. Если говорить совсем простым языком, корутина — это функция, перед определением которой стоит ключевое слово async. Если посмотреть на type() этой функции, мы увидим, что она принадлежит к классу coroutine.

async def function():
    pass
print(type(function()))

>>> <class 'coroutine'>

Цикл событий работает исключительно с корутинами; простую функцию туда передать не получится. Всё, что нам нужно, — это передать список задач tasks вместе с корутиной в цикл событий. Дальше в курсе мы разберём, как это делать.

Существует также ключевое слово await, которое даёт циклу событий понять, что на этой задаче можно переключаться, пока первая задача ожидает ответа от сервера или же пока вы сидите в засаде на фазана.

Если рассмотреть картину в целом, асинхронность выполняет код точно так же шаг за шагом. Однако в моменты ожидания она переключается на другие задачи, чтобы инициировать их выполнение. Пока мы ждём ответа на первый запрос к серверу, можем отправить ещё один запрос, затем ещё один и будем ждать выполнения до тех пор, пока не получим ответ на последний запрос.

Не стоит думать, что цикл событий способен обрабатывать только заранее подготовленный список задач. На самом деле, мы с лёгкостью можем создать бесконечный цикл событий и добавлять в него задачи по мере необходимости. Всё как в жизни. :)



Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: