Вступление

Асинхронное программирование - это тип параллельного программирования, при котором единице работы разрешается запускаться отдельно от основного потока приложения. Когда работа завершена, он уведомляет основной поток о завершении или сбое рабочего потока. Его использование дает множество преимуществ, таких как улучшенная производительность приложений и повышенная скорость отклика.

Асинхронное программирование привлекает много внимания в последние несколько лет, и не зря. Хотя это может быть сложнее, чем традиционный линейный стиль, он также намного эффективнее.

Например, вместо ожидания завершения HTTP-запроса перед продолжением выполнения, с помощью асинхронных сопрограмм Python вы можете отправить запрос и выполнить другую работу, ожидающую в очереди, пока ожидает завершения HTTP-запроса.

Асинхронность, кажется, главная причина того, почему Node.js так популярен для серверного программирования. Большая часть кода, который мы пишем, особенно в тяжелых приложениях ввода-вывода, таких как веб-сайты, зависит от внешних ресурсов. Это может быть что угодно, от удаленного вызова базы данных до POSTing и до службы REST. Как только вы запрашиваете какой-либо из этих ресурсов, ваш код ждет, ничего не делая. При асинхронном программировании вы позволяете своему коду обрабатывать другие задачи, ожидая ответа от других ресурсов.

Как Python выполняет несколько задач одновременно?

1. Несколько процессов

Самый очевидный способ - использовать несколько процессов. Из терминала вы можете запустить свой сценарий два, три, четыре… ​​десять раз, и тогда все сценарии будут выполняться независимо или одновременно. Операционная система, расположенная ниже, позаботится о совместном использовании ресурсов вашего ЦП между всеми этими экземплярами. В качестве альтернативы вы можете использовать библиотеку multiprocessing, которая поддерживает процессы порождения, как показано в примере ниже.

Выход:

2. Несколько потоков

Следующий способ запустить несколько задач одновременно - использовать потоки. Поток - это строка выполнения, очень похожая на процесс, но у вас может быть несколько потоков в контексте одного процесса, и все они имеют общий доступ к общим ресурсам. Но из-за этого сложно написать код потоковой передачи. И снова операционная система выполняет всю тяжелую работу по совместному использованию ЦП, но глобальная блокировка интерпретатора (GIL) позволяет только одному потоку запускать код Python в данный момент времени, даже если у вас есть несколько потоков, выполняющих код. Итак, в CPython GIL предотвращает многоядерный параллелизм. По сути, вы работаете на одном ядре, даже если у вас их два, четыре или больше.

Выход :

3. Сопрограммы, использующие yield:

Сопрограммы - это обобщение подпрограмм. Они используются для совместной многозадачности, когда процесс добровольно передает (уступает) управление периодически или во время простоя, чтобы позволить нескольким приложениям работать одновременно. Сопрограммы похожи на генераторы, но с несколькими дополнительными методами и небольшими изменениями в том, как мы используем оператор yield. Генераторы производят данные для итерации, в то время как сопрограммы также могут потреблять данные.

Выход :

4. Асинхронное программирование

Четвертый способ - это асинхронное программирование, в котором ОС не участвует. Что касается ОС, у вас будет один процесс, и в нем будет один поток, но вы сможете делать несколько вещей одновременно. Итак, в чем трюк?

Ответ: asyncio.

asyncio - новый модуль параллелизма, представленный в Python 3.4. Он разработан для использования сопрограмм и фьючерсов, чтобы упростить асинхронный код и сделать его почти таким же читаемым, как и синхронный код, поскольку здесь нет обратных вызовов.

asyncio использует разные конструкции: циклы событий, сопрограммы и фьючерсы.

  • цикл событий управляет и распределяет выполнение различных задач. Он регистрирует их и обрабатывает распределение потока управления между ними.
  • Сопрограммы (описанные выше) - это специальные функции, которые работают аналогично генераторам Python, при await они возвращают поток управления событию. петля. Необходимо запланировать запуск сопрограммы в цикле событий после того, как запланированные сопрограммы обернуты в Задачи, которые являются типом Будущее.
  • Futures представляют собой результат задачи, которая могла быть выполнена или нет. Этот результат может быть исключением.

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

Переключение контекста в asyncio представляет цикл событий, передающий поток управления от одной сопрограммы к другой.

В этом примере мы запускаем 3 асинхронных задачи, которые запрашивают Reddit отдельно, извлекают и распечатывают JSON. Мы используем aiohttp, клиентскую библиотеку http, обеспечивающую асинхронное выполнение даже HTTP-запросов.

Выход :

Использование Redis и Redis Queue RQ

Использование asyncio и aiohttp не всегда возможно, особенно если вы используете более старые версии python. Также будут сценарии, когда вы захотите распределить свои задачи по разным серверам. В этом случае мы можем использовать RQ (Redis Queue). Это простая библиотека Python для постановки заданий в очередь и их обработки в фоновом режиме с помощью рабочих. Он поддерживается Redis - хранилищем данных типа ключ / значение.

В приведенном ниже примере мы поставили в очередь простую функцию count_words_at_url с помощью Redis.

Выход:

Вывод:

Возьмем классический пример - шахматную выставку, где один из лучших шахматистов соревнуется с множеством людей. А если есть 24 партии с 24 людьми, и шахматный мастер играет со всеми синхронно, то это займет не менее 12 часов (с учетом того, что средняя партия занимает 30 ходов, шахматный мастер думает 5 секунд. придумать ход и противник - примерно 55 секунд). Но использование асинхронного режима дает шахматисту возможность сделать ход и оставить оппонента думать, переходя к следующему и делая там ход. Таким образом, ход во всех 24 играх можно сделать за 2 минуты, а все игры можно выиграть всего за один час.

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

По этой аналогии, шахматным мастером будет наш ЦП, и идея состоит в том, что мы хотим убедиться, что ЦП не ждет и не ждет как можно меньше времени. Речь идет о том, чтобы всегда находить, чем заняться.

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

*****************************************************************

Эта запись изначально была опубликована в блоге Velotio

Velotio Technologies - партнер по аутсорсингу разработки программных продуктов для технологических стартапов и предприятий. Мы специализируемся на разработке продуктов B2B и SaaS для предприятий, уделяя особое внимание искусственному интеллекту и машинному обучению, DevOps и тестированию.

Хотите узнать о нас больше? Мы хотели бы связаться с вами на нашем Веб-сайте, LinkedIn или Twitter.

*****************************************************************