причина
Возникновение асинхронности в основном однопотоковое io ожидание.Так как большинство задач ждут io, то если разрешить работу одному потоку, то все задачи выполняются в конвейерном виде.Если один запрос занимает 1 секунду, а пять запросов - пять секунд , то если можно Пускать одновременно, то скорость можно увеличить в пять раз
Есть два способа, как выполнять пять задач одновременно
- Многопоточность
- асинхронный
Любой, кто отлаживал слишком много потоков, знает, что поток копирует основной поток с нуля. Открытие нескольких потоков не только затратно, но и дорого для отладки. Асинхронный режим отличается. Вы можете запрограммировать его как один поток. чем многопоточность
Несколько реализаций асинхронного Python
Существует много фреймворков для реализации асинхронности в Python, но основная идея, вероятно, основана на следующих двух способах:
- twister
- gevent
Идея твистера в том, чтобы инкапсулировать асинхронные операции и работать через обратные вызовы.scrapy
Реализация промежуточного запросаtwister
Способ
scrapy.Request(url='xxx', callback=func)
инкапсулируется путем прохожденияrequest
, когда фреймворк выполнит для нас запрос, он перезвонит через обратный вызов.Если ваш запрос простой, это нормально, вам нужно перезвонить только один раз.Если ваш запрос сложный, то вы будете вводить回调地狱(callback hell)
И вам также нужно написать для обработки исключений, генерируемых различными обратными вызовами, вы можете видетьscrapy
Реализация промежуточного программного обеспечения знаетscrapy
Насколько громоздкой является обработка исключений. Но наличие середины облегчает нам кодирование модулей, поэтому не будем говорить об этом здесь.
twister
Этот тип обратного вызова является античеловеческим. Он должен полагаться на ядро, стоящее за ним, для планирования. Без поддержки ядра, стоящего за ним, он вообще не может работать, и поскольку он полагается на обратные вызовы для последующих шагов, наш код должен быть разделены на разные части.часть, если мы не знаем, как ядро за функциями обратного вызова или ограничениями, мы не знаем, что эти две функции вообще связаны
Этот метод программирования более благоприятен для разработки модулей, но для нас, знакомых с последовательным программированием, этот метод обратного вызова, очевидно, является кошмаром по сравнению сtwister
Этот метод обратного вызова,gevent
Обратные вызовы выполнены в виде зеленых сопрограмм.
PEP-380 определяетyield from
Утверждение, Python3.3 начал использовать, чтобы различать сопрограммы и генераторы, Python3.5 начал использоватьawait
заменятьyield from
, чтобы у сопрограммы был специальный метод для объявления (await
а такжеasync
), который используется для обозначения асинхронных функций
Большая часть причины, по которой сопрограммы могут быть блестящими в асинхронности, заключается в том, что сопрограммы по своей природе асинхронны.Чтобы понять сопрограммы, мы можем сравнить их из простого генератора с обычными функциями.
a = (x for x in range(10))
b = [x for x in range(10))
Давайте посмотрим на такой генератор а. Обычно, когда мы используем этот генератор, мы должны добавитьfor
цикл, чтобы получить значение внутри, если мы попытаемся использоватьa.send(None)
, мы обнаружим, что получаем последовательность в b из возвращаемого значения в свою очередь
Именно такая функция отправки и приема позволяет нам реализовать «зеленый» обратный вызов, то есть природа сопрограммы делает более логичной запись асинхронно, и по сравнению сtwister
Обратный вызов, обратный вызов сопрограммы более тщательный, он оборачивает «я» и перезванивает всем.
Узнайте об основах асинхронности
Я вкратце рассказал о природе сопрограмм, а теперь рассказываю об основах асинхронного существования.Самое главное для асинхронного существования — это ожидание.Чтобы понять смысл ожидания и последующую интерпретациюasycio
библиотека, мы сначала используемselectors
(пара Python3select
пакет) для демонстрации
import selectors
sel = selectors.DefaultSelector()
объявить одинselect
object sel, теперь мы хотим вызвать эту основную функцию
sel.select(10)
Это 10 является представителемtimeout
Длина времени, то есть самое долгое время ожидания, через 10 секунд мы обнаружили, что этот результат вернул пустой список, что очевидно, мы не указали, что он ждал
selectors
Функцию этой библиотеки очень легко понять, она аналогична отправке письма: если вы хотите дождаться ответа от кого-то еще, если вы не отправляете свое письмо, значит, вы ждали в почтовом ящике, если только вы не не хотите ждать, иначе вас не примут.Ответьте, так ядро этой библиотеки "отправить письмо" (зарегистрироваться) и дождаться письма (выбрать), а затем выбрать обработку письма самостоятельно
import selectors
import socket
sel = selectors.DefaultSelector()
def accept(sock, mask):
conn, addr = sock.accept()
print('accepted', conn, 'from', addr)
sock = socket.socket()
sock.bind(('localhost', 8000))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
Самое главное в этой программе то, чтоsel.register
,sel.select
а такжеcallback
Там первая — функция регистрации, вторая — ожидание, а последняя — обратный вызов
вышеtwister
Самый простой обратный вызов, как видите, для того, чтобы получить соединениеsock
соединение, мы должны зарегистрировать процесс ожидания, но это простоsock
соединение, чтобы успешно установитьTCP
Для подключения нам все равно придется делать трехэтапное рукопожатие, и нам придется разбираться с ошибками при каждом обратном вызове
И вы можете увидеть функцию обратного вызова и основной драйверselect.select()
Степень связанности очень высока, мы должны полностью понимать, как система перезванивает, а обработка одной вещи разбивается на секции обратным вызовом.
Далее мы смотрим наgevent
изasyncio
выполнить
async def wget(host):
connect = asyncio.open_connection(host, 80)
reader, writer = await connect
header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
writer.write(header.encode('utf-8'))
await writer.drain()
while True:
line = await reader.readline()
if line == b'\r\n':
break
writer.close()
Мы успешно использовали функцию для описания процесса установления соединения и общения, если вы немного понимаетеasyncio
, вы обнаружите, что это то же самое, что иtwister
Обратный вызов отличается, используйтеawait
Ключевое слово приостанавливает функцию, затем ожидает обратного вызова, а затем выполняет следующие операции в соответствии с обратным вызовом.Мы успешно написали асинхронно с синхронными операторами, и мы использовали нативную реализацию Python, поэтому, когдаasyncio
Как гордился Гвидо (отец Python), когда он вышел, вы можете увидеть цитату ниже.Tulip: Async I/O for Python 3Видео выступления
Анализ асинхронной реализации Python
Ранее мы знали, что основа асинхронности ждет, так как же Гвидо реализует асинхронность с помощью сопрограмм?Далее кратко поговорим об основе этой реализации.
Мы сначала ставим вышеtwister
изменить наgevent
Способ
sel = selectors.DefaultSelector()
@asyncio.coroutine
def get_connection(sock):
sel.register(sock, selectors.EVENT_READ)
yield True
async def create_connection():
sock = socket.socket()
sock.bind(('localhost', 8000))
sock.listen(100)
sock.setblocking(False)
await get_connection(sock)
conn, addr = sock.accept()
print('accepted', conn, 'from', addr)
event = create_connection()
event.send(None)
events = sel.select(100)
for key, mask in events:
try:
event.send(None)
except StopIteration:
pass
Немного изменим вышесказанноеtwister
функцию, мы создаемget_connection
функция положитьsock
привязать к нашемуsel
выше, а затем перезвонитеTrue
, конечно, этот обратный вызов не обрабатывает исключения или что-то еще, и тогда мы заставим сопрограмму отправить емуNone
пусть это начнется, теперь вы находитесь в другойipython
Клиентское исполнение
import socket
socket.socket()..connect(('localhost', 8000))
Затем вы обнаружите, что информация о подключении клиента распечатывается в основном потоке.
Из этого небольшого примера мы знаем, что проблема, которую необходимо решить при реализации асинхронности, — это общедоступный регистр (который может регистрировать все ожидания ввода-вывода), контейнер (в котором могут храниться все сопрограммы) и ядро, которое всегда может выполнять ожидающие обратные вызовы и обработку. обратные вызовы (несколько сопрограмм)
Глубокое погружение в asyncio, чтобы понять асинхронность Python
Благодаря вышеизложенному мы просто знаем, как использовать сопрограммы сselect
Сотрудничайте для выполнения асинхронных операций, но то, что мы написали выше, это только самая базовая реализация, давайте углубимсяasyncio
Исходный код, чтобы узнать, как упростить асинхронность
Цитировать
Фреймворк асинхронного параллелизма Python
Асинхронное программирование на Python: Asyncio
Tulip: Async I/O for Python 3
【Перевод】Углубленное понимание библиотеки Asyncio в python3.4 и механизма асинхронного ввода-вывода в Node.js.