Существует 3 способа делать что-либо асинхронно:
- Отдельный процесс
- Отдельный поток
- Асинхронный код
Это именно отдельный процесс выполнения программы, разные процессы хорошо работают в Linux.
Отдельный процесс - хорошо
Он использует все ресурсы ОС, он изолирован от других процессов, и они могут выполняться по-настоящему параллельно.
Отдельный процесс - плохо
У разных процессов нет разделяемых переменных, они полностью изолированы друг от друга, если мы делаем что то одно, то разным процессам требуется синхронизация друг с другом, и они очень затратны, по скольку это отдел программа для них берется свои собственные участки памяти, и для каждого отдел процесса берется свой интерпретатор, и зависят от количества ядер.
Где использовать отдел процессы
- Обработка изображений
- Обработка больших массивы данных в DS
- Обработка матриц
Для работы используется модуль multiprocessing его класс Pool
для создания разных процессов.
Хорошо работают в винде, все потоки работают последовательно, просто передают управление от потока в поток.
Отдельный потоки - хорошо
- Имеют общую память за счет того что запускаются в одном процессе
- Общие переменные
- Не требуют много память
- Хорошо подходят для операций с сетью, делать запросы к серверам.
Отдельный потоки - плохо
- У отдела потоков есть GIL -
- Нельзя использовать CPU-bound функции - то есть если есть какие-то долгие сложные и вычисления, то из-за вмешательства GIL в разных потоках они будут работать т дольше чем просто последовательно.
- Код не тривиален.
- В работе нескольких потоках сразу могут возникать конфликты потоков.
Где использовать отдел потоки
- Работа с сетью
Для работы есть модуль threading
Асинхронный код умеет работать в одном процессе и в одном потоке, для асинхронного кода требуется 2 вещи:
- Функции, что умеют засыпать и просыпаться в нужное время по команде.
- Также требуется диспетчер, в асинхронном коде он называется Event Loop. этот диспетчер принимает асинхронном функции, решает какой из них исполнятся и передает управление следующей функции.
Асинхронность - хорошо
- Выполняется в одном процессе и одном потоке
- Малое потребление ресурсов
- Независим от ОС
Асинхронность - плохо
- Сложность отладки
- Исключение можно поймать только в внутри асинхронной функции
- Сложно понять в каком контексте происходит работа
- Сложно читаемый код
- Легко допустить ошибку
Есть 2 библиотеки для работы с асинхронностью asyncIo gevent
Корутины (Сопрограммы)
Корутина (специализированная функция-генератор) - является основой для асинхронного программирования
Суть Асинхронности
Многопроцессорность - означает распараллеливание работы между частями центрально вычислительной системой, между процессорами и ядрами.
Многопоточность - суть в том что задача разбивается на множество потоков, занимающихся её решением, один процесс может содержать в себе множество потоков. Она прекрасно решает задачи связанные с вводом/выводом (IO)
В интерпретатор Cpython введен новый тип работы, при помощи 2 ключевых слов async и await. Подобная асинхронность тоже есть в других языках как Go, C# и Scala.
Модуль asyncio - поддерживает одновременное выполнение задачи, но он не использует ни многопроцессорность ни многопоточность. Он использует один процесс один поток, и называет это кооперативная многозадачность.
Асинхронность позволяет ставить работу части программы на паузу при помощи диспетчера Event Loop и передавать работу другой части программы, в этом асинхронность похожа больше на многопоточность, которая тоже работает с перерывами, так асинхронность и многопоточность выражают философию борьбы между своими потоками/асинх.функциями в то время как многопроцессорность именно параллели процесс работы.
Кооперативная многозадачность - можно объяснить так, это один цикл который работает со множеством заданий, давая им оптимальное время на работу.
Асинхронность удобна когда есть периоды ожидания, которые бы тратили время в пустую при синхронном выполнении.
Материал - https://realpython.com/python-concurrency/
Корутины
Сердце асинхронного программирования - это корутины. Корутины - это специальная версия функций-генераторов в Python.
Корутина это функция которая перед возвращением значения может прервать свое выполнение и передать контроль другой корутине.
Модуль AsyncIO await/async
Библиотека asyncio пользует 2 ключ слова await/async. Вот пример реализации:
import asyncio, time
async def counter():
print('one')
await asyncio.sleep(1)
print('two')
async def main():
await asyncio.gather(counter(), counter(), counter())
time_1 = time.perf_counter()
asyncio.run(main())
time_2 = time.perf_counter()
time_final = time_2 - time_1
print(f'Время выполнения программы: {time_final:0.5f}')
# Вывод
# one
# one
# one
# two
# two
# two
# Время выполнения программы: 1.00132И так в чем тут суть, если мы используем обычный time.sleep(1) он просто
заблокирует выполнение, как бы имитируя сложную долгую обработку сервиса,
в то время как asyncio.sleep(1) тоже имитирует долгую обработку, но на
момент этого ожидания программа не ожидает, а переходит к выполнению
другой функции корутины.
В такие моменты программа только ожидает выполнение и ничего не делает, и в этот самый момент времени мы и экономим, так образ достигаем илюзии параллельной работы, действительности это своего рода простая оптимизация.
Правила работы с асинхронным кодом
async def - называется естественной корутиной или асинхронным генератором.
Также есть и другие механизмы этого спец слова async with и async for
и они тоже работают, для своих целей.
await - Ключевое слово указывает функции что она будет отдавать контроль
обратно в главный цикл работы, этот главный цикл в свою очередь содержит
порядок выполнения всех зарегистрированных корутин.
Функция помеченная как async def может использовать одно из
await return yield. await можно использовать только в теле самой
корутины.
Для того чтобы пометить как await этот обьект должен быть awaitable
для этого это должна быть либо просто функция или обьект реализующий
метод __await__
WebSocket — протокол связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени.
Говоря о JavaScript
В js есть 2 механизма для работы в живом времени, это Веб-сокеты и Socket.IO.
Веб-сокеты - стандартный вариант, открывает TCP-соединение, держит его открытым передавая информацию в реальном времени, их стоит использовать в след случаях:
- Чаты
- Многопользовательские игры
- Совместное редактирование
- Социальные (новостные) ленты
- Приложения, работающие на основе местоположения
- Торговые площадки,
Socket.IO — библиотека JavaScript, основанная (написанная поверх) на веб-сокетах, использует веб соккеты если они доступны, или другием методы типа: Flash Socket, AJAX Long Polling, AJAX Multipart Stream
Эта библиотека удобнее в использовании, к примеру может сразу отправлять сообщение всем участникам сразу, при использовании веб-сокетов, для реализации подобной задачи вам потребуется список подключенных клиентов и отправка сообщений по одному.
Socket в Python
Модуль socket для общения между клиентом и сервером, в качестве
клиента может выступать и другой сервер, создаем класс для работы
с сокетами:
Общее
socket.socket(socket.AF_INET, socket.SOCK_STREAM) - Создание сокета.
Для Сервера
sock.bind((HOST_ALL, PORT)) - Сервер принимает соединение от клиента.
sock.listen(5) - Количество клиентов для связи.
connection, addres = sock.accept() - создание соединения.
Цикл получения и ответа на сообщение от клиента.
while True:
data = connection.recv(1024)
if not data:
break
print(f'Получено от клиента: {data.decode()}')
message = 'Ответ от сервера'
connection.send(message.encode())Для Клиента
Соединение клиента с сервером:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
message = input('Ваше сообщение серверу: ')
sock.send(message.encode())
data = sock.recv(1024)
print(f'Пришло от сервера: {data.decode()}')Общее
connection.shutdown(1); connection.close() - закрываем соединение