Как работает модуль asyncio, почему мой обновленный образец работает синхронно?
Я попробовал следующий код в Python 3.6 для asyncio: Пример 1:
import asyncio
import time
async def hello():
print('hello')
await asyncio.sleep(1)
print('hello again')
tasks=[hello(),hello()]
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
Выход как и ожидалось:
hello
hello
hello again
hello again
Затем я хочу изменить asyncio.sleep в другой def:
async def sleep():
time.sleep(1)
async def hello():
print('hello')
await sleep()
print('hello again')
tasks=[hello(),hello()]
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
Выход:
hello
hello again
hello
hello again
Кажется, он работает не в асинхронном режиме, а в обычном режиме синхронизации.
Вопрос в том, почему он не работает в асинхронном режиме и как я могу изменить старый модуль синхронизации на "асинхронный"?
1 ответ
Asyncio использует цикл обработки событий, который выбирает, какую задачу (независимую цепочку вызовов сопрограмм) в очереди для активации следующей. Цикл событий может принимать разумные решения относительно того, какая задача готова выполнить реальную работу. Вот почему цикл обработки событий также отвечает за создание соединений и просмотр файловых дескрипторов и других примитивов ввода / вывода; он дает представление о цикле событий, когда выполняются операции ввода-вывода или когда результаты доступны для обработки.
Всякий раз, когда вы используете await
, есть возможность вернуть управление в цикл, который затем может передать управление другой задаче. Какая задача будет выбрана для выполнения, зависит от конкретной реализации; asyncio
Эталонная реализация предлагает несколько вариантов, но есть и другие реализации, такие как очень, очень эффективная реализация uvloop.
Ваш образец все еще асинхронный. Так уж сложилось, что, заменив await.sleep()
с синхронным time.sleep()
Вызов, внутри новой функции сопрограммы, вы добавили 2 сопрограммы в цепочку вызовов задачи, которые не дают, и, таким образом, влияют на то, в каком порядке они выполняются. То, что они выполняются в том, что кажется синхронным порядком, является совпадением. Если вы переключили циклы событий или добавили больше сопрограмм (особенно тех, которые используют ввод / вывод), порядок снова может легко измениться.
Кроме того, ваши новые сопрограммы используют time.sleep()
; это делает ваши сопрограммы неэффективными. Цикл событий не уведомляется о том, что ваш код ожидает (time.sleep()
не уступит!), поэтому никакие другие сопрограммы не могут быть выполнены, пока time.sleep()
бежит. time.sleep()
просто не возвращает или запускает любой другой код, пока не истечет запрошенное время. Сравните это с asyncio.sleep()
реализация, которая просто уступает в цикле событий с call_later()
крюк; Цикл обработки событий теперь знает, что этой задаче не потребуется никакого внимания до более позднего времени.
Также см. Asyncio: почему он не является неблокирующим по умолчанию, чтобы более подробно обсудить взаимодействие задач и цикла событий. И если вам нужно запустить блокирующий, синхронный код, который нельзя заставить взаимодействовать, то используйте пул исполнителей, чтобы код блокировки выполнялся в отдельном шаге или дочернем процессе, чтобы освободить цикл обработки событий для других, лучше управляемых задач.