Reputation: 15010
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_time
considered a coroutine (as in contains yield
statement?
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.?
Upvotes: 1
Views: 211
Reputation: 39626
- 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]
Upvotes: 1