Самый простой пример async/wait, возможно, в Python
Я читал много примеров, сообщений в блогах, вопросов/ответов об asyncio
/async
/await
в Python 3. 5+, многие из них были сложными, простейшее, что я нашел, вероятно, было таким. Тем не менее он использует ensure_future
и для обучения асинхронному программированию в Python, я хотел бы увидеть, возможен ли еще более минимальный пример (т.е. Какие минимальные инструменты необходимы для выполнения базового примера async/await).
Вопрос: для целей обучения асинхронному программированию в Python можно привести простой пример, показывающий, как работает async
/await
, используя только эти два ключевых слова + asyncio.get_event_loop()
+ run_until_complete
+ другой код Python, но никаких других функций asyncio
?
Пример: что-то вроде этого:
import asyncio
async def async_foo():
print("async_foo started")
await asyncio.sleep(5)
print("async_foo done")
async def main():
asyncio.ensure_future(async_foo()) # fire and forget async_foo()
print('Do some actions 1')
await asyncio.sleep(5)
print('Do some actions 2')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
но без ensure_future
и все еще демонстрирует, как работает await/async.
Ответы
Ответ 1
можно ли дать простой пример, показывающий, как работает async
/await
, используя только эти два ключевых слова + asyncio.get_event_loop()
+ run_until_complete
+ другой код Python, но никаких других функций asyncio
?
Таким образом, можно написать код, который работает:
import asyncio
async def main():
print('done!')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Но таким образом невозможно продемонстрировать, почему вам нужен асинчо.
Кстати, зачем вам asyncio
, а не просто код? Ответ. - asyncio
позволяет вам получать выгоду от производительности при параллелизации операций блокировки ввода-вывода (например, чтение/запись в сеть). И чтобы написать полезный пример, вам нужно использовать асинхронную реализацию этих операций.
Пожалуйста, прочитайте этот ответ для более подробного объяснения.
Upd:
ok, здесь пример, который использует asyncio.sleep
для имитации операции блокировки ввода-вывода и asyncio.gather
который показывает, как вы можете одновременно выполнять несколько операций блокировки:
import asyncio
async def io_related(name):
print(f'{name} started')
await asyncio.sleep(1)
print(f'{name} finished')
async def main():
await asyncio.gather(
io_related('first'),
io_related('second'),
) # 1s + 1s = over 1s
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Вывод:
first started
second started
first finished
second finished
[Finished in 1.2s]
Обратите внимание на то, как оба io_related
начали, а затем через одну секунду, оба сделаны.
Ответ 2
Чтобы ответить на ваши вопросы, я предоставлю 3 разных решения одной и той же проблемы.
случай 1: просто нормальный питон
import time
def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
def sum(name, numbers):
total = 0
for number in numbers:
print(f'Task {name}: Computing {total}+{number}')
sleep()
total += number
print(f'Task {name}: Sum = {total}\n')
start = time.time()
tasks = [
sum("A", [1, 2]),
sum("B", [1, 2, 3]),
]
end = time.time()
print(f'Time: {end-start:.2f} sec')
выход:
Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3
Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6
Time: 5.02 sec
дело 2: асинхронное/ожидание выполнено неправильно
import asyncio
import time
async def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
async def sum(name, numbers):
total = 0
for number in numbers:
print(f'Task {name}: Computing {total}+{number}')
await sleep()
total += number
print(f'Task {name}: Sum = {total}\n')
start = time.time()
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(sum("A", [1, 2])),
loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
end = time.time()
print(f'Time: {end-start:.2f} sec')
выход:
Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3
Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6
Time: 5.01 sec
case 3: async/await выполнено правильно (так же, как в случае 2, за исключением функции sleep
)
import asyncio
import time
async def sleep():
print(f'Time: {time.time() - start:.2f}')
await asyncio.sleep(1)
async def sum(name, numbers):
total = 0
for number in numbers:
print(f'Task {name}: Computing {total}+{number}')
await sleep()
total += number
print(f'Task {name}: Sum = {total}\n')
start = time.time()
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(sum("A", [1, 2])),
loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
end = time.time()
print(f'Time: {end-start:.2f} sec')
выход:
Task A: Computing 0+1
Time: 0.00
Task B: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task B: Computing 1+2
Time: 1.00
Task A: Sum = 3
Task B: Computing 3+3
Time: 2.00
Task B: Sum = 6
Time: 3.01 sec
case 1
с case 2
дают то же самое 5 seconds
, тогда как case 3
просто 3 seconds
. Так что async/await done right
быстрее.
Причина различий заключается в реализации функции sleep
.
# case 1
def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
# case 2
async def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
# case 3
async def sleep():
print(f'Time: {time.time() - start:.2f}')
await asyncio.sleep(1)
Функции sleep
в case 1
и case 2
"одинаковы".
Они "спят", не позволяя другим использовать ресурсы.
Принимая во внимание, что case 3
разрешает доступ к ресурсам, когда он спит.
В case 2
мы добавили async
к нормальной функции. Однако цикл обработки событий запустит его без перерыва.
Почему? Потому что мы не сказали, где циклу разрешено прерывать вашу функцию для запуска другой задачи.
В case 3
мы указали в цикле событий, где именно нужно прервать функцию для запуска другой задачи. Где именно?
# case 3
async def sleep():
print(f'Time: {time.time() - start:.2f}')
await asyncio.sleep(1) # <-- Right here!
Подробнее об этом читайте здесь here