#python-3.x #python-asyncio
#python-3.x #python-asyncio
Вопрос:
Я просматривал документацию Python для asyncio
и мне интересно, почему в большинстве примеров используется loop.run_until_complete()
в отличие от Asyncio.ensure_future()
.
Например:https://docs.python.org/dev/library/asyncio-task.html
Кажется, ensure_future
это был бы гораздо лучший способ продемонстрировать преимущества неблокирующих функций. run_until_complete
с другой стороны, блокирует цикл, как это делают синхронные функции.
Это заставляет меня чувствовать, что я должен использовать run_until_complete
вместо комбинации ensure_future
with loop.run_forever()
для одновременного запуска нескольких подпрограмм.
Комментарии:
1.
run_until_complete
ничего не блокирует. Разница между ним иrun_forever
заключается в том, что цикл приостанавливается при завершении сопрограммы. Единственный раз, когда он будет заблокирован, — это если ваша сопрограмма никогда не ожидает.2. Я написал это pastebin.com/Qi8dQ3bh и, похоже, это блокирует цикл.
do_other_things()
не выполняется доdo_io()
завершения, даже еслиdo_io()
ожидает перехода в режим ожидания.3. Это потому, что в цикле больше ничего не было запланировано. Попробуйте вызвать
loop.create_task(do_other_things())
перед вызовомrun_forever
.
Ответ №1:
run_until_complete
используется для запуска future до его завершения. Это заблокирует выполнение следующего за ним кода. Однако это приводит к запуску цикла событий. Все запланированные фьючерсы будут выполняться до тех пор, пока не будет выполнено будущее, переданное run_until_complete
.
Учитывая этот пример:
import asyncio
async def do_io():
print('io start')
await asyncio.sleep(5)
print('io end')
async def do_other_things():
print('doing other things')
loop = asyncio.get_event_loop()
loop.run_until_complete(do_io())
loop.run_until_complete(do_other_things())
loop.close()
do_io
будет выполняться. После его завершения do_other_things
запустится. Ваш вывод будет:
io start
io end
doing other things
Если вы запланируете do_other_things
цикл событий перед запуском do_io
, управление переключится с do_io
на do_other_things
, когда первый ожидает.
loop.create_task(do_other_things())
loop.run_until_complete(do_io())
Это даст вам результат:
doing other things
io start
io end
Это потому, что do_other_things
было запланировано ранее do_io
. Существует множество разных способов получения одного и того же результата, но какой из них имеет смысл, действительно зависит от того, что на самом деле делает ваше приложение. Поэтому я оставлю это в качестве упражнения для читателя.
Комментарии:
1. Вы знаете, почему я получаю ошибку «RuntimeError: этот цикл событий уже запущен» при запуске вашего кода?
2. @Patrick ты пробовал вызывать
loop.run_until_complete
изнутри функции?3. Я понял, что проблема, возможно, с Jupyter. Это работает с кодом python.
4. @Patrick (поздно) но у меня есть сцена this и используется
loop = asyncio.new_event_loop()
(хотя разрешен только один цикл событий в main)5.
get_event_loop
не рекомендуется: docs.python.org/3/library /…
Ответ №2:
Я думаю, что большинство людей не поняли create_task
. когда вы create_task
или ensure_future
, это уже будет запланировано.
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
task1 = asyncio.create_task(
say_after(1, 'hello')) # not block here
task2 = asyncio.create_task(
say_after(2, 'world'))
print(f"started at {time.strftime('%X')}") # time0
await task1 # block here!
print(f"finished at {time.strftime('%X')}")
await task2 # block here!
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
результат
time0
print hello
time0 1
print world
time0 2
но ЕСЛИ ВЫ НЕ ОЖИДАЕТЕ task1, сделайте что-нибудь еще
async def main():
task1 = asyncio.create_task(
say_after(1, 'hello')) # not block here
print(f"finished at {time.strftime('%X')}") # time0
await asyncio.sleep(2) # not await task1
print(f"finished at {time.strftime('%X')}") # time0 2
asyncio.run(main())
он ВСЕ еще выполняет задачу 1
time0
print hello
time0 2
Комментарии:
1. Хорошо, не забудьте импортировать время импорта asyncio
2. Этот ответ хорош, потому что он использует современный Python 3.10 asyncio.create_task/asyncio.run. Сведение его только к этим двум вместе с asyncio.gather, asyncio.sleep и async / await сами по себе значительно упрощают.