Asyncio Event Loops Tutorial
Table Of Contents
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.
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.
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
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()
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
If you found this tutorial useful or you require more assistance then please feel free to leave a comment in the comments section below!