The coroutine provided to asyncio.run() to start the asyncio event loop is called the main coroutine or the main task.
It has special properties, such as when it is done, the asyncio event loop is shut down and all other tasks are canceled.
In this tutorial, you will discover the main coroutine in asyncio programs.
Let’s get started.
What is the Main Coroutine
A coroutine is a function that can be suspended and resumed.
It is often defined as a generalized subroutine.
A subroutine can be executed, starting at one point and finishing at another point. Whereas, a coroutine can be executed then suspended and resumed many times before finally terminating.
You can learn more about Python coroutines in the tutorial:
An asyncio program is started explicitly by starting the event loop.
Typically, this involves calling the asyncio.run() module function and providing a coroutine object to execute as the entry point to the program.
For example:
1 2 3 |
... # start the event loop asyncio.run(main()) |
The coroutine that is provided to asyncio.run() is called the main coroutine.
- Main Coroutine: The coroutine provided to asyncio.run() when starting the asyncio event loop.
Note, this does not refer to coroutines executed via the newer asyncio.Runner context manager that is able to execute an arbitrary number of coroutines in the event loop and there is no main coroutine.
You can learn more about the asyncio.Runner context manager in the tutorial:
The asyncio documentation sometimes refers to the main coroutine as the “main task”:
For example:
The Runner creates the main task for the passed coroutine for its execution.
— Asyncio Runners
The main coroutine is synonymous with the “main thread” and the “main process“.
Recall that the main thread is the entry point into a program or the default thread, and has the name “MainThread“.
You can learn more about the main thread in the tutorial:
Also, recall that the main process is the process that encloses the main thread and is the first or main parent process created when the Python interpreter is started and has the name “MainProcess“.
You can learn more about the main process in the tutorial:
Both the main thread and the main process have special properties, and so does the main coroutine.
Run loops using all CPUs, download your FREE book to learn how.
Special Properties of the Main Coroutine
The main coroutine has special properties compared to other asyncio tasks and coroutines.
- The main coroutine is the entry point into the asyncio event loop.
- When the main coroutine exits, all other tasks are canceled and the asyncio event loop is terminated.
- When a SIGINT is raised in the program the main task is called via the cancel() method.
Some differences between the main thread and the main process include:
- The main coroutine does not have a special name e.g. it is typically called “Task-1” as the first task created.
- The main coroutine does not have a special type, e.g. it is an asyncio.Task and can be canceled like any other task.
The Most Important Property of the Main Coroutine
The most important detail of the main coroutine is that when it is done, the event loop is closed.
Recall, that the main coroutine can be done in a few ways:
- It may be completed normally.
- It may return early.
- It may raise an unhandled exception.
- It may be canceled.
- It may raise an unhandled signal.
Once done, the asyncio event loop is terminated.
As part of the shutdown, all other tasks in the event loop are canceled.
This is important because if we want to immediately shut down the event loop on demand, we must do so by completing the main coroutine.
You can learn more about this in the tutorial:
Now that we are familiar with the main coroutine, let’s look at a worked example.
Free Python Asyncio Course
Download your FREE Asyncio PDF cheat sheet and get BONUS access to my free 7-day crash course on the Asyncio API.
Discover how to use the Python asyncio module including how to define, create, and run new coroutines and how to use non-blocking I/O.
Example of Reporting Details of Main Coroutine
We can explore an example of reporting the details of the main coroutine.
In this example, we will define a main coroutine that gets the asyncio.Task object for the current task and reports its type and details.
We expect that it is not special or different from a normal asyncio task, e.g. unlike how the main thread and main process are different from normal thread and process objects.
Will then await a second work coroutine that reports the same details.
The complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# SuperFastPython.com # report the details of the main coroutine import asyncio # function to report task details def report_task_details(): # get the current task task = asyncio.current_task() # report task type print(type(task)) # report task details print(task) # another coroutine async def work(): # report the task details report_task_details() # main coroutine async def main(): # report the task details report_task_details() # run the other coroutine await asyncio.create_task(work()) # start the asyncio event loop asyncio.run(main()) |
Running the example starts the asyncio event loop and runs the main() coroutine.
Our main() coroutine is taken as the “main coroutine” of the program.
The main() coroutine runs and calls our report_task_details() function to report task details. The function reports the data type for the main() coroutine, and then reports the details of the task object.
Next, the work() coroutine is scheduled as a new asyncio.Task and awaited by the main coroutine.
The work() task calls our report_task_details() function and reports the type and details of itself.
We can see that both the main coroutine and the new work() task have the same data type of “_asyncio.Task“.
The results highlight that the asyncio.Task type for the main coroutine is not special or different from a normal asyncio task.
1 2 3 4 |
<class '_asyncio.Task'> <Task pending name='Task-1' coro=<main() running at ...:22> cb=[_run_until_complete_cb() at .../asyncio/base_events.py:180]> <class '_asyncio.Task'> <Task pending name='Task-2' coro=<work() running at ...:17> cb=[Task.task_wakeup()]> |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Further Reading
This section provides additional resources that you may find helpful.
Python Asyncio Books
- Python Asyncio Mastery, Jason Brownlee (my book!)
- Python Asyncio Jump-Start, Jason Brownlee.
- Python Asyncio Interview Questions, Jason Brownlee.
- Asyncio Module API Cheat Sheet
I also recommend the following books:
- Python Concurrency with asyncio, Matthew Fowler, 2022.
- Using Asyncio in Python, Caleb Hattingh, 2020.
- asyncio Recipes, Mohamed Mustapha Tahrioui, 2019.
Guides
APIs
- asyncio — Asynchronous I/O
- Asyncio Coroutines and Tasks
- Asyncio Streams
- Asyncio Subprocesses
- Asyncio Queues
- Asyncio Synchronization Primitives
References
Takeaways
You now know about the main coroutine in asyncio programs.
Did I make a mistake? See a typo?
I’m a simple humble human. Correct me, please!
Do you have any additional tips?
I’d love to hear about them!
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Solé Bicycles on Unsplash
Do you have any questions?