python - terminar - ¿Cómo crear un bucle de eventos con coroutines rodantes ejecutándose para siempre?
for i in range python español (4)
¿Realmente intentaste ejecutar los tres ejemplos que diste? La diferencia en el comportamiento es bastante obvia.
Como nunca dijiste lo que esperas, no se dice qué es correcto y qué no lo es. Las tres implementaciones pueden ser correctas o incorrectas. Puedo decirle qué comportamiento tiene cada implementación y por qué tiene tal comportamiento; Sólo tú sabes si es correcto.
En el segundo ejemplo ( yield from asyncio.sleep(1)
), las dos corrutinas se ejecutan simultáneamente. Esto significa que cada uno ejecutará por su cuenta; hello_world
imprime Hello World
cada segundo y good_evening
imprime Good Evening
cada segundo.
Los otros dos ejemplos usan time.sleep(1)
, que está bloqueando. Esto significa que cuando la primera función (cualquiera que sea; digamos que es hello_world
) llega a time.sleep(1)
, el programa completo se bloqueará durante un segundo . Esto significa que cuando hello_world
duerme, good_evening
tampoco puede ejecutarse, y viceversa.
El programa se ejecuta así:
- Se introduce el bucle.
- El bucle llama a
hello_world
. -
time.sleep(1)
enhello_world
. El programa duerme un segundo. -
Hello World
impreso. -
hello_world
cede. - El bucle llama
good_evening
. -
Good Evening
impresas. -
time.sleep(1)
engood_evening
. El programa duerme un segundo. -
good_evening
rendimientos. - Ir a 2.
Por lo tanto, tanto Hello World
como Good Evening
aparecen cada dos segundos, porque hay dos time.sleep(1)
entre cada print
. Notarías fácilmente que si ejecutas los dos ejemplos.
Para evitar el cambio de contexto, quiero crear un gran bucle para atender tanto las conexiones de red como algunas rutinas.
Aquí está la implementación para las funciones 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()
Aquí está la implementación para 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()
Y el mixto:
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()
Como puedes ver, cada función de coroutine tiene un bucle while rodeado. ¿Cómo puedo hacerlo como el normal? Es decir, cuando está listo, se llama después del tiempo de retardo dado, pero no solo pone un bucle allí.
Si realmente desea eliminar el bucle while de las coroutinas (no estoy seguro de por qué cree que es necesario; es la forma más natural de hacer lo que está tratando de hacer), puede usar asyncio.async
(o asyncio.ensure_future
on Python 3.4.4+) para programar la rutina para que se ejecute de nuevo en la siguiente iteración del bucle de eventos:
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()
Tenga en cuenta que debe volver a usar loop.run_forever()
si hace esto, ya que hello_world
/ good_evening
se good_evening
inmediatamente después de imprimir ahora.
# 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()
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
Este código alcanzaría la máxima profundidad de recursión. Podría ser porque Python no tiene optimización de llamadas de cola. Deje el código aquí como un ejemplo incorrecto.