I am trying to wrap my head around python asyncio.
from datetime import datetime
import asyncio
def print_time(number, loop):
et = loop.time() + 10.0
while True:
print('loop {} time {}'.format(number, datetime.now()))
if (loop.time() >= et):
break
yield from asyncio.sleep(3)
loop = asyncio.get_event_loop()
asyncio.ensure_future(print_time(1,loop))
asyncio.ensure_future(print_time(2,loop))
loop.run_forever()
The output (from Jupyter notebook)
loop 1 time 2017年06月11日 21:38:47.398280
loop 2 time 2017年06月11日 21:38:47.398452
loop 1 time 2017年06月11日 21:38:50.401818
loop 2 time 2017年06月11日 21:38:50.402335
loop 1 time 2017年06月11日 21:38:53.405770
loop 2 time 2017年06月11日 21:38:53.406243
loop 1 time 2017年06月11日 21:38:56.409524
loop 2 time 2017年06月11日 21:38:56.410034
loop 1 time 2017年06月11日 21:38:59.413300
loop 2 time 2017年06月11日 21:38:59.413846
I have the following questions about the above program
In this case is the function
print_timeconsidered a coroutine (as in containsyieldstatement?does ensure_future() creates a separate task/process each time it is called? In that sense is it analogous to fork()
The current loop is running for ever. How do I gracefully exit after say 10 iterations.?
1 Answer 1
- In this case is the function print_time considered a coroutine (as in contains yield statement?
You can create coroutine from function when you need it (for example, if it should await another coroutine). To create coroutine you should use special decorator @asyncio.coroutine (or define coroutine with async def in Python 3.5+):
@asyncio.coroutine
def print_time(number, loop):
...
In your case generator considered as coroutine only because asyncio's coroutines implemented using generators. You shouldn't use regular generators as coroutines, instead you should always explicitly use decorator @asyncio.coroutine (or async def) to create coroutines.
- does ensure_future() creates a separate task/process each time it is called? In that sense is it analogous to fork()
ensure_future doesn't create OS thread or process. Usually all your coroutines/tasks run in single OS thread, single process.
ensure_future is a way to rule flow of execution your async script. When you just await for some coroutine, code below it would be run only after coroutine finished. ensure_future allows you to "run coroutine in background" without blocking code below it from execution.
Example 1:
yield from coro_1()
yield from coro_2() # this line would be started only after coro_1 finished.
Example 2:
asyncio.ensure_future(coro_1())
yield from coro_2() # this line would be started immediately.
# coro_1 and coro_2 would de facto run parallely.
- The current loop is running for ever. How do I gracefully exit after say 10 iterations.?
You have many ways to do it, depending of how you want your code to work.
Here's one, for example:
from datetime import datetime
import asyncio
# We should somehow limit number of iterations for this coroutine.
# For example we can pass it as param 'tries':
@asyncio.coroutine
def print_time(number, loop, tries):
et = loop.time() + 10.0
for _ in range(tries):
print('loop {} time {}'.format(number, datetime.now()))
if (loop.time() >= et):
break
yield from asyncio.sleep(3)
@asyncio.coroutine
def main():
# Run to tasks parallely and await both done using 'asyncio.gather':
yield from asyncio.gather(
asyncio.ensure_future(print_time(1, loop, tries=5)),
asyncio.ensure_future(print_time(2, loop, tries=5))
)
loop = asyncio.get_event_loop()
# Use 'run_until_complete' to finish execution when some coroutine is done:
loop.run_until_complete(main())
Output:
loop 1 time 2017年06月11日 15:16:03.864059
loop 2 time 2017年06月11日 15:16:03.864059
loop 1 time 2017年06月11日 15:16:06.864826
loop 2 time 2017年06月11日 15:16:06.864826
loop 1 time 2017年06月11日 15:16:09.865046
loop 2 time 2017年06月11日 15:16:09.865046
loop 1 time 2017年06月11日 15:16:12.865199
loop 2 time 2017年06月11日 15:16:12.865199
loop 1 time 2017年06月11日 15:16:15.865978
loop 2 time 2017年06月11日 15:16:15.865978
[Finished in 12.2s]