web-dev-qa-db-fra.com

Exemple le plus simple async/wait possible en Python

J'ai lu de nombreux exemples, billets de blog, questions/réponses concernant asyncioasync/await EN PYTHON 3.5+, BEAUCOUP ÉTAIENT COMPLEXES, LE PLUS SIMPLE QUE J'AI TROUVÉ ÉTAIT PROBABLEMENT CELUI-CI . NÉANMOINS, IL UTILISE ensure_future ET, POUR L’APPRENTISSAGE DE LA PROGRAMMATION ASYNCHRONE EN PYTHON, JE VOUDRAIS VOIR SI UN EXEMPLE ENCORE PLUS MINIMAL EST POSSIBLE (C’EST-À-DIRE QUELS SONT LES OUTILS MINIMUM NÉCESSAIRES POUR FAIRE UN EXEMPLE DE BASE ASYNC/WAIT ).

QUESTION: À DES FINS D'APPRENTISSAGE SUR LA PROGRAMMATION ASYNCHRONE EN PYTHON, EST-IL POSSIBLE DE DONNER UN EXEMPLE SIMPLE/_ MONTRANT COMMENT FONCTIONNE async/await, en n'utilisant que ces deux mots clés + asyncio.get_event_loop() + run_until_complete +, mais aucune autre fonction asyncio?

Exemple: quelque chose comme ceci:

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

mais sans ensure_future, et montre toujours comment wait/async fonctionne.

9
Basj

est-il possible de donner un exemple simple montrant comment asyncawait fonctionne en utilisant uniquement ces deux mots clés + asyncio.get_event_loop() + run_until_complete + autre code Python mais pas d'autres fonctions asyncio?

De cette façon, il est possible d'écrire du code qui fonctionne:

import asyncio


async def main():
    print('done!')


if __==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

_/Mais de cette façon, il est impossible de démontrer pourquoi vous avez besoin d'asyncio.

À propos, pourquoi avez-vous besoin de asyncio, pas seulement du code brut? Answer is - asyncio vous permet d’obtenir des avantages en termes de performances lorsque vous parallélisez des opérations de blocage d’E/S (telles que la lecture/l’écriture sur le réseau). Et pour écrire des exemples utiles, vous devez utiliser une implémentation asynchrone de ces opérations.

Veuillez lire cette réponse pour une explication plus détaillée.

Upd:

ok, voici un exemple qui utilise asyncio.sleep pour imiter l'opération de blocage d'E/S et asyncio.gather qui montre comment vous pouvez exécuter plusieurs opérations de blocage simultanément:

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 __==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Sortie:

first started
second started
first finished
second finished
[Finished in 1.2s]

Notez comment les deux io_related ont alors commencé, après une seconde à la fois.

8

Pour répondre à vos questions, je proposerai 3 solutions différentes au même problème.

cas 1: juste un python normal

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

sortie:

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

cas 2: async/attend mal fait

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

sortie:

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

cas 3: async/wait done right (identique au cas 2 sauf la fonction 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')

sortie:

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 avec case 2 donne le même 5 seconds, alors que case 3 ne contient que 3 seconds. Donc, le async/await done right est plus rapide.

La raison de la différence est dans l'implémentation de la fonction 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)

La fonction sleep dans case 1 et case 2 sont les mêmes. Ils "dorment" sans permettre à d'autres d'utiliser les ressources . Tandis que case 3 permet d'accéder aux ressources lorsqu'il est endormi.

Dans case 2, nous avons ajouté async à la fonction normale. Cependant, la boucle d’événement l’exécutera sans interruption. Pourquoi? Parce que nous n'avons pas indiqué où la boucle est autorisée à interrompre votre fonction pour exécuter une autre tâche.

Dans case 3, nous avons indiqué à la boucle d'événements exactement où interrompre la fonction pour exécuter une autre tâche. Où exactement?

# case 3
async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.sleep(1) # <-- Right here!

Plus sur ceci lire ici

2
Levon