Простейший пример асинхронности / ожидания в Python - PullRequest
0 голосов
/ 08 июня 2018

Я прочитал много примеров, сообщений в блоге, вопросов / ответов о asyncio / async / await в Python 3.5+, многие были сложными, самый простой, который я нашел, был, вероятно, этот .Тем не менее он использует ensure_future, и для изучения асинхронного программирования в Python я хотел бы посмотреть, возможен ли еще более минимальный пример (т. Е. Какие минимальные инструменты необходимы для выполнения базовой асинхронной /пример ожидания).

Вопрос: для целей обучения об асинхронном программировании в 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.

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Чтобы ответить на ваши вопросы, я предоставлю 3 различных решения одной и той же проблемы.

case 1: просто обычный python

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')

output:

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

дело 3: выполнено асинхронное / ожиданиеправый (аналогично случаю 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!

Подробнее об этом читайте здесь

0 голосов
/ 08 июня 2018

Можно ли привести простой пример, показывающий, как работает 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, а не просто код?Ответ - asyncio позволяет получить выигрыш в производительности, когда вы распараллеливаете операции блокировки ввода-вывода (например, чтение / запись в сеть).И чтобы написать полезный пример, вам нужно использовать асинхронную реализацию этих операций.

Пожалуйста, прочитайте этот ответ для более подробного объяснения.

Upd:

хорошо, вот пример, который использует 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 начались тогда, через одну секунду, оба сделали.

...