Asyncio Event Loops Tutorial
This tutorial was built on top of Python 3.6.
In this tutorial we are going to be covering Asyncio’s event loop. Some of the material for this tutorial was taken from my book: Learning Concurrency in Python.
Video
The Event Loop
The main component of any asyncio based Python program has to be the underlying event loop. Within this event loop we can (from the official documentation):
- register, execute and cancel calls
- Launch subprocesses and the associated transports for communication with an external program
- Delegate costly function calls to a pool of threads
Essentially all an event loop does is wait for events to happen before matching each event to a function that we have explicitly matched with said type of event.
A good example of this would be a simple web server, say we have an endpoint on our server that serves our website which features a multitude of different pages. Our event loop essentially listens for requests to be made and then matches each of these requests to its associated webpage.
Each of the requests made to our web server in the above example would be
considered a separate event
. These events are then matched to a set function
that we have predefined whenever a said event is triggered.
Getting Started
Let’s take a quick look at how you can define a very simple event loop. In order
to instantiate an event loop we’ll use asyncio.get_event_loop()
, we’ll then
start a try... finally
and within the body of our try
we’ll specify that we
want our newly instantiated event loop to run until it has completed our
myCoroutine()
function.
import asyncio
## Define a coroutine that takes in a future
async def myCoroutine():
print("My Coroutine")
## Spin up a quick and simple event loop
## and run until completed
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(myCoroutine())
finally:
loop.close()
Running Options
We have a number of options for running our event loops, we can either call
run_forever()
which will subsequently run our event loop until the stop()
function is called, or we can call run_until_complete(future)
and only run our
event loop until whatever future
object we’ve passed in has completed it’s
execution.
The run_until_complete() method
Let’s take a quick look at the run_until_complete()
function. In this example
we’ll define our myWork()
coroutine which we will then pass into our
run_until_complete
function and subsequently we should see our event loop run
until this myWork()
coroutine is finished it’s execution.
import asyncio
import time
async def myWork():
print("Starting Work")
time.sleep(5)
print("Finishing Work")
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(myWork())
finally:
loop.close()
Upon running this you should then see the following output:
$ python3.6 test.py
Starting Work
Finishing Work
The run_forever() method
The alternative way of starting up your event loop is to call the
run_forever()
method which will subsequently start your asyncio based event
loop and have it run indefinitely until the program comes to an end or the
stop()
method is called. It should be noted that calling this causes our main
thread to block indefinitely.
Let’s take a look at a quick example which showcases the use of this method.
We’ll first define our work()
coroutine which will feature a while loop that
will run indefinitely and simply print out Task Executed
in 1 second
intervals.
import asyncio
async def work():
while True:
await asyncio.sleep(1)
print("Task Executed")
loop = asyncio.get_event_loop()
try:
asyncio.ensure_future(work())
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print("Closing Loop")
loop.close()
Running Multiple coroutines:
If you wanted to run multiple coroutines indefinitely in parallel then you can
do that by creating your x
number of coroutines and have them run a while loop
each. You would then call asyncio.ensure_future(function())
in order to
enqueue them onto the loop and they would run indefinitely after that point.
import asyncio
import time
async def firstWorker():
while True:
await asyncio.sleep(1)
print("First Worker Executed")
async def secondWorker():
while True:
await asyncio.sleep(1)
print("Second Worker Executed")
loop = asyncio.get_event_loop()
try:
asyncio.ensure_future(firstWorker())
asyncio.ensure_future(secondWorker())
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print("Closing Loop")
loop.close()
This should output the following indefinitely:
$ python3.6 run-forever.py
First Worker Executed
Second Worker Executed
First Worker Executed
Second Worker Executed
First Worker Executed
Second Worker Executed
Conclusion
If you found this tutorial useful or you require more assistance then please feel free to leave a comment in the comments section below!