### Блокирующие операции Блокирующими считаются те операции, при выполнении которых процесс вынужден ждать ее окончания, прежде чем начать выполнять следующий код. Это происходит из-за того, что приостанавливается работа event loop. Большинство синхронных методов относятся к блокирующим, и чаще всего они выполняют операции ввода/вывода или работают с сетью. Вслучае блокировки результат не будет получен до тех пор, пока запрос не будет выполнен. ### Неблокирующие операции Легко спутать неблокирующие операции с асинхронными операциями, однако это разные концепции, которые действительно хорошо работают вместе. Например, представьте очередь на почту и есть клиент X кому необходимо получить пенсию, но пенсии еще не привезли. Работник, вместо того, чтобы ждать пока привезут пенсии и блокировать посещение других клиентов до момента, просто сигнализирует клиенту X вернуться в другую дату и продолжает обслуживание. Неблокирующий оператор - это оператор, который при минимальном знаке блокировки возвращает управляющий код или исключение, указывающее повторить попытку позже. ### Асинхронные операции Возвращаясь к примеру с почтой. Представьте, что у каждого работника есть 10 помощников для выполнения долгих задач. По мере поступления клиентов, если у клиента X есть запрос, который может заблокировать очередь на неограниченное количество времени, этот запрос отправляется помощнику, который выполнит работу в фоновом режиме и обратится непосредственно к клиенту X, когда его или ее ответ будет готов, тем самым освобождая работника для обработки запроса от следующего клиента, не дожидаясь выполнения предыдущего. Асинхронные операции уведомляют об окончании запросов с помощью обратных вызовов(callback), сопрограмм(coroutines) и других механизмов. Функция обратного вызова - это функция, которая вызывается при возникновении определенного условия. Он обычно используется для обработки результатов асинхронной обработки. ### Цикл событий(event loop) Чтобы понять концепцию цикла событий, нам нужно понять из чего он состоит. Будем использовать термин "дескриптор ресурса" для обозначения дескриптора сокета или дескриптора файла. #### Функции опроса (Polling functions) Метод опроса реализуется различными операционными системами с целью мониторинга состояния одного или нескольких "дескрипторов ресурсов". Системы реализуют этот метод с помощью функций. Функции опроса формируют основу циклов событий. Мы часто можем обнаружить, что эти модели называются схемой уведомления о готовности из-за того факта, что функция опроса уведомляет того, кто заинтересован в событии, о том, что дескриптор ресурса готов к взаимодействию, однако заинтересованный может или не может выполнить желаемую операцию. В Linux есть следующие функции опроса: * `select()`: это POSIX реализация, которая имеет несколько недостатков * Ограничение количества ресурсов для мониторингу * Сложность O(n), где n это количество подключенных клиентов, что делает невозможным одновременное обслуживание нескольких клиентов серверами. * `poll()`: это усовершенствованный ответ на select() со следующими функциями: * Позволяет отслеживать более широкий диапазон дескрипторов ресурсов * Сложность O(n) как у select() * Позволяет использовать большее разнообразие типов отслеживаемых событий * Повторно использует вводимые данные в своем вызове, в отличие от select() * `epoll()`: Это Linux реализация, которая имеет постоянную сложность O(1). Функция epoll() предлагает два варианта поведения для отслеживания событий с помощью вызова epoll_wait(). Чтобы определить эти два поведения, давайте представим ситуацию, в которой у нас есть производитель(producer), записывающий данные в сокет (с соответствующим дескриптором сокета), и потребитель(consumer), ожидающий завершения чтения данных: * Срабатывание по уровне(level-triggered): когда потребитель выполняет вызов `epoll_wait()`, он получит статус этого дескриптора ресурса, немедленно возвращенный запрошенному событию, указывающий на возможность (или нет) выполнения операции чтения (в нашем случае). Таким образом, поведение, вызванное уровнем, напрямую связано со статусом события, а не с самим событием. * Cрабатывания по фронту (edge-triggered). Вызов `epoll_wait()` вернется только тогда, когда событие записи в сокет завершится и данные будут доступны. Таким образом, в поведении, по фронту, основное внимание уделяется самому произошедшему событию, а не возможности выполнения какого-либо события. На других платформах также доступны функции опроса, такие как `kqueue` для BSD и Mac OS X. Функции опроса работают в следующем порядке: 1. Создается объект опроса (poller). Poller - это интерфейс, который обеспечивает абстракцию для использования функций опроса. 2. Мы можем зарегистрировать один или несколько дескрипторов ресурсов в объекте опроса(poller). 3. Функция опроса выполняется в созданном объекте опроса(poller). #### Использование циклов событий Мы можем определить циклы событий как абстракции, которые упрощают использование функций опроса для мониторинга событий. Внутренне циклы событий используют объекты опроса, снимая с программиста ответственность за управление задачами добавления, удаления и управления событиями. Циклы событий, как правило, используют функции обратного вызова для обработки возникновения события; например, учитывая дескриптор ресурса A, когда событие записи происходит в A, для него будет функция обратного вызова. #### Использование asyncio asyncio позволяет реализовать асинхронное программирование с использованием комбинации следующих элементов: - Цикл событий - Сопрограммы. Сопрограмма - это генератор, который следует определенным соглашениям. Его наиболее интересной особенностью является то, что он может быть приостановлен во время выполнения, чтобы дождаться внешней обработки (некоторой процедуры ввода-вывода) и вернуться с точки, на которой он остановился, когда внешняя обработка завершена. - Futures. Futures представляют собой обработчик, который еще не завершен. - Tasks. Это подкласс asyncio.Futures для инкапсуляции сопрограмм и управления ими.