In order to prevent from context switching, I want to create a big loop to serve both the network connections and some routines.
Here's the implementation for normal functions:
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()
Here's the implementation for 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()
And the mixed one:
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()
As you see, each coroutine function has a while loop surrounded. How can I make it like the normal one? I.e. when it is done, call itself after the given delay time, but not just put a loop there.
Did you actually try to run the three examples you gave? The difference in behaviour is pretty obvious.
Since you never said what you expect, there’s not telling what is right and what is not. All three implementations could be right or wrong. I can tell you what behaviour each implementation has, and why it has such behaviour; only you know whether it is correct.
In the second example (
yield from asyncio.sleep(1)
), the two coroutines are run concurrently. This means that each will execute on their own;hello_world
printsHello World
every second, andgood_evening
printsGood Evening
every second.The other two examples both use
time.sleep(1)
, which is blocking. This means that when the first function (whichever that is; let’s say it’shello_world
) reachestime.sleep(1)
, the whole program will hang for one second. This means that whenhello_world
sleeps,good_evening
cannot run either, and vice versa.The program executes like this:
hello_world
.time.sleep(1)
inhello_world
is reached. The program sleeps for one second.Hello World
printed.hello_world
yields.good_evening
.Good Evening
printed.time.sleep(1)
ingood_evening
is reached. The program sleeps for one second.good_evening
yields.Therefore both
Hello World
andGood Evening
appear every two seconds, because there are twotime.sleep(1)
calls between eachprint
. You would easily notice that if you run the two examples.If you really want to eliminate the while-loop from the coroutines (I'm not sure why you feel that's necessary; it's the most natural way to do what you're trying to do), you can use
asyncio.async
(orasyncio.ensure_future
on Python 3.4.4+) to schedule the coroutine to run again on the next event loop iteration:Note that you have to switch back to using
loop.run_forever()
if you do this, sincehello_world
/good_evening
will exit immediately after printing now.UPD
This code would reach the maximum recursion depth. Might because Python has no tail call optimization. Leave the code here as a wrong example.