web-dev-qa-db-fra.com

Comment créer une boucle d'événement avec des coroutines roulantes fonctionnant pour toujours?

Afin d'éviter toute commutation de contexte, je souhaite créer une grande boucle pour desservir les connexions réseau et certaines routines.

Voici l'implémentation pour les fonctions normales:

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)

try:
    # Blocking call interrupted by loop.stop()
    print('step: loop.run_forever()')
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Voici l'implémentation pour les coroutines:

import asyncio


@asyncio.coroutine
def hello_world():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World')

@asyncio.coroutine
def good_evening():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world(),
        good_evening()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Et le mixte:

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

@asyncio.coroutine
def hello_world_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World Coroutine')

@asyncio.coroutine
def good_evening_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening Coroutine')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
print('step: asyncio.async(hello_world_coroutine)')
asyncio.async(hello_world_coroutine())
print('step: asyncio.async(good_evening_coroutine)')
asyncio.async(good_evening_coroutine())

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Comme vous le voyez, chaque fonction de coroutine est entourée d’une boucle while. Comment puis-je le faire comme le normal? C'est à dire. quand c'est fait, appelez-vous après le délai imparti, mais ne vous contentez pas de mettre une boucle.

19
changyuheng

Si vous voulez vraiment éliminer la boucle while des coroutines (je ne vois pas trop pourquoi vous estimez que c'est nécessaire; c'est la façon la plus naturelle de faire ce que vous essayez de faire), vous pouvez utiliser asyncio.async ( ou asyncio.ensure_future sur Python 3.4.4+) pour planifier l'exécution de la coroutine à la prochaine itération de la boucle d'événement:

import asyncio

@asyncio.coroutine
def hello_world():
    yield from asyncio.sleep(1)
    print('Hello World')
    asyncio.async(hello_world())

@asyncio.coroutine
def good_evening():
    yield from asyncio.sleep(1)
    print('Good Evening')
    asyncio.async(good_evening())

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    asyncio.async(hello_world())
    asyncio.async(good_evening())
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Notez que vous devez revenir à l’utilisation de loop.run_forever() si vous procédez ainsi, car hello_world/good_evening se ferme immédiatement après l’impression.

18
dano
# asyncio_coroutine_forever.py

import asyncio

async def hello_world():

    await asyncio.sleep(1)
    print('Hello World')
    await good_evening()


async def good_evening():

    await asyncio.sleep(1)
    print('Good Evening')
    await hello_world()


loop = asyncio.get_event_loop()

try:

    loop.run_until_complete(hello_world())
    loop.run_until_complete(good_evening())
    loop.run_forever()

finally:

    print('closing event loop')
    loop.close()
3
Down the Stream
import asyncio


@asyncio.coroutine
def hello_world_coroutine():
    yield from asyncio.sleep(1)
    print('Hello World Coroutine')
    yield from hello_world_coroutine()

@asyncio.coroutine
def good_evening_coroutine():
    yield from asyncio.sleep(1)
    print('Good Evening Coroutine')
    yield from good_evening_coroutine()

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world_coroutine(),
        good_evening_coroutine()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

UPD

Ce code atteindrait la profondeur de récursivité maximale. Peut-être parce que Python n’a pas d’optimisation des appels de queue. Laissez le code ici comme un mauvais exemple.

1
changyuheng

Avez-vous réellement essayé de présenter les trois exemples que vous avez donnés? La différence de comportement est assez évidente.

Puisque vous n’avez jamais dit ce que vous attendiez, il n’est pas dit ce qui est juste et ce qui ne l’est pas. Les trois implémentations peuvent être bonnes ou mauvaises. Je peux vous dire quel comportement a chaque implémentation et pourquoi elle a un tel comportement; vous seul savez si c'est correct.


Dans le deuxième exemple (yield from asyncio.sleep(1)), les deux coroutines sont exécutées simultanément. Cela signifie que chacun s'exécutera seul; hello_world imprime Hello World toutes les secondes et good_evening imprime Good Evening toutes les secondes.

Les deux autres exemples utilisent tous deux time.sleep(1), qui bloque. Cela signifie que lorsque la première fonction (quelle qu’elle soit; disons que ce soit hello_world) atteigne time.sleep(1), tout le programme sera suspendu pendant une seconde. Cela signifie que lorsque hello_world est en sommeil, good_evening ne peut pas fonctionner non plus, et inversement.

Le programme s'exécute comme ceci:

  1. La boucle est entrée.
  2. La boucle appelle hello_world.
  3. time.sleep(1) in hello_world est atteint. Le programme dort une seconde.
  4. Hello World imprimé.
  5. hello_world donne.
  6. La boucle appelle good_evening.
  7. Good Evening imprimé.
  8. time.sleep(1) in good_evening est atteint. Le programme dort une seconde.
  9. good_evening donne.
  10. Aller à 2.

Par conséquent, Hello World et Good Evening apparaissent tous les deux secondes, car il existe deux appels time.sleep(1) entre chaque print. Vous remarquerez facilement que si vous exécutez les deux exemples.

0
uranusjr