Last Updated on November 14, 2023
Asynchronous tasks run independently in the asyncio event loop.
We may need to stop or cancel a task from executing after it has started. This may be for many reasons, such as we no longer require the result, to the running task may negatively impact the program.
In this tutorial, you will discover how to cancel asyncio tasks.
After completing this tutorial, you will know:
- How to request that a task cancel.
- What happens when a task is cancelled and the conditions under which a cancelled task may not stop.
- How a message can be provided to a cancelled task.
Let’s get started.
What is an Asyncio Task
An asyncio Task is an object that schedules and independently runs an asyncio coroutine.
It provides a handle on a scheduled coroutine that an asyncio program can query and use to interact with the coroutine.
A Task is an object that manages an independently running coroutine.
— PEP 3156 – Asynchronous IO Support Rebooted: the “asyncio” Module
An asyncio task is represented via an instance of the asyncio.Task class.
A task is created from a coroutine. It requires a coroutine object, wraps the coroutine, schedules it for execution, and provides ways to interact with it.
A task is executed independently. This means it is scheduled in the asyncio event loop and will execute regardless of what else happens in the coroutine that created it. This is different from executing a coroutine directly, where the caller must wait for it to complete.
Tasks are used to schedule coroutines concurrently. When a coroutine is wrapped into a Task with functions like asyncio.create_task() the coroutine is automatically scheduled to run soon
— Coroutines and Tasks
We can create a task using the asyncio.create_task() function.
This function takes a coroutine instance and an optional name for the task and returns an asyncio.Task instance.
Wrap the coro coroutine into a Task and schedule its execution. Return the Task object.
— Coroutines and Tasks
For example:
1 2 3 |
... # create and schedule a task task = asyncio.create_task(coro) |
You can learn more about asyncio tasks in the tutorial:
Now that we know about asyncio tasks, let’s look at how we might cancel a task.
Run loops using all CPUs, download your FREE book to learn how.
How to Cancel a Task
We can cancel a scheduled task via the cancel() method.
The cancel method returns True if the task was canceled, or False otherwise.
For example:
1 2 3 |
... # cancel the task was_cancelled = task.cancel() |
If the task is already done, it cannot be canceled and the cancel() method will return False and the task will not have the status of canceled.
The next time the task is given an opportunity to run, it will raise a CancelledError exception.
If the CancelledError exception is not handled within the wrapped coroutine, the task will be canceled.
Otherwise, if the CancelledError exception is handled within the wrapped coroutine, the task will not be canceled.
The cancel() method can also take a message argument which will be used in the content of the CancelledError.
Now that we know how to cancel a task, let’s look at some worked examples.
Example of Canceling a Running Task
We can explore how to cancel a running task.
In this example, we define a task coroutine that reports a message and then blocks for a moment.
We then define the main coroutine that is used as the entry point into the asyncio program. It reports a message, creates and schedules the task, then waits a moment.
The main coroutine then resumes and cancels the task while it is running. It waits a moment more to allow the task to respond to the request to cancel. The main coroutine then reports whether the request to cancel the task was successful.
The task is canceled and is then done.
The main coroutine then reports whether the status of the task is canceled before closing the program.
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 28 29 30 31 32 |
# SuperFastPython.com # example of canceling a running task import asyncio # define a coroutine for a task async def task_coroutine(): # report a message print('executing the task') # block for a moment await asyncio.sleep(1) # custom coroutine async def main(): # report a message print('main coroutine started') # create and schedule the task task = asyncio.create_task(task_coroutine()) # wait a moment await asyncio.sleep(0.1) # cancel the task was_cancelled = task.cancel() # report whether the cancel request was successful print(f'was canceled: {was_cancelled}') # wait a moment await asyncio.sleep(0.1) # check the status of the task print(f'canceled: {task.cancelled()}') # report a final message print('main coroutine done') # start the asyncio program asyncio.run(main()) |
Running the example starts the asyncio event loop and executes the main() coroutine.
The main() coroutine reports a message, then creates and schedules the task coroutine.
It then suspends and awaits a moment to allow the task coroutine to begin running.
The task runs, reports a message and sleeps for a while.
The main() coroutine resumes and cancels the task. It reports that the request to cancel the task was successful.
It then sleeps for a moment to allow the task to respond to the request to be canceled.
The task_coroutine() resumes and a CancelledError exception is raised that causes the task to fail and be done.
The main() coroutine resumes and reports whether the task has the status of canceled. In this case, it does.
This example highlights the normal case of canceling a running task.
1 2 3 4 5 |
main coroutine started executing the task was canceled: True canceled: True main coroutine done |
Next, let’s take a look at how we might cancel a scheduled task.
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 Canceling a Scheduled Task
A scheduled task is a task that has been created and scheduled in the event loop but has not yet had an opportunity to run.
That is, the task is not yet “running“.
A scheduled task can be canceled in the same manner as a running task.
If a scheduled task is canceled, it will raise a CancelledError exception when it is given an opportunity to run.
The example below updates the previous example so that the scheduled task is canceled before it has an opportunity to run.
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 28 29 30 |
# SuperFastPython.com # example of canceling a scheduled task import asyncio # define a coroutine for a task async def task_coroutine(): # report a message print('executing the task') # block for a moment await asyncio.sleep(1) # custom coroutine async def main(): # report a message print('main coroutine started') # create and schedule the task task = asyncio.create_task(task_coroutine()) # cancel the task before it has had a chance to run was_cancelled = task.cancel() # report whether the cancel request was successful print(f'was canceled: {was_cancelled}') # wait a moment await asyncio.sleep(0.1) # check the status of the task print(f'canceled: {task.cancelled()}') # report a final message print('main coroutine done') # start the asyncio program asyncio.run(main()) |
Running the example starts the asyncio event loop and executes the main() coroutine.
The main() coroutine reports a message, then creates and schedules the task coroutine.
The main() coroutine then cancels the task before it has had an opportunity to execute. It reports that the request to cancel the task was successful.
The main() coroutine then sleeps for a moment to allow the task to respond to the request to be canceled.
The task_coroutine() runs and a CancelledError exception is raised, which causes the task to fail and be done.
The main() coroutine resumes and reports whether the task has the status of canceled. In this case it does.
This example highlights the normal case of canceling a running task.
This highlights that both a scheduled task can be canceled in the same manner as a running task.
1 2 3 4 |
main coroutine started was canceled: True canceled: True main coroutine done |
Next, let’s take a look at canceling a done task.
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of Canceling a Done Task
We cannot cancel a done task.
If a task is not scheduled or running, it cannot be canceled.
If we attempt to cancel a done task, the request will fail, meaning that the cancel() method will return False. If we check the status of the task, it will not be marked as canceled.
We can explore this with a worked example.
In the example below, a task is created and scheduled, then awaited until it is done. It is then canceled.
The expectation is that a request to cancel the done task will fail and that the status of the task will not be set to canceled.
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 28 29 30 31 32 |
# SuperFastPython.com # example of canceling a done task import asyncio # define a coroutine for a task async def task_coroutine(): # report a message print('executing the task') # block for a moment await asyncio.sleep(1) # custom coroutine async def main(): # report a message print('main coroutine started') # create and schedule the task task = asyncio.create_task(task_coroutine()) # wait for the task to be done await task # cancel the task was_cancelled = task.cancel() # report whether the cancel request was successful print(f'was canceled: {was_cancelled}') # wait a moment await asyncio.sleep(0.1) # check the status of the task print(f'canceled: {task.cancelled()}') # report a final message print('main coroutine done') # start the asyncio program asyncio.run(main()) |
Running the example starts the asyncio event loop and executes the main() coroutine.
The main() coroutine reports a message, then creates and schedules the task coroutine. It then awaits the task until it is done.
The main() coroutine then attempts to cancel the task.
The request fails. The cancel() method returns False, indicating the request failed and the value is reported.
The main() coroutine then checks if the status of the task is canceled, which it is not.
This highlights that we cannot cancel a done task nor change a successful task to done by calling the cancel() method.
1 2 3 4 5 |
main coroutine started executing the task was canceled: False canceled: False main coroutine done |
Next, we will look at how to handle a request to be canceled within a task.
Example of a Task Handling the Request to Cancel
A task can handle a response to being canceled.
This can be achieved by expecting and handling an asyncio.CancelledError exception.
If a coroutine wrapped in a task handles the CancelledError then the task will not be canceled, may continue to run, and will not be marked with the canceled status meaning the cancelled() method will return False.
This means that although the cancel() method may return True indicating that the task will be canceled, it does not mean that that will be canceled, only that the task has the potential to be canceled.
The example below gives an example of canceling a task while it is running and the task handling the request for canceling by reporting a message.
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 28 29 30 31 32 33 34 35 |
# SuperFastPython.com # example of a task handling the request to be canceled import asyncio # define a coroutine for a task async def task_coroutine(): try: # report a message print('executing the task') # block for a moment await asyncio.sleep(1) except asyncio.CancelledError: print('Received a request to cancel') # custom coroutine async def main(): # report a message print('main coroutine started') # create and schedule the task task = asyncio.create_task(task_coroutine()) # wait a moment await asyncio.sleep(0.1) # cancel the task was_cancelled = task.cancel() # report whether the cancel request was successful print(f'was canceled: {was_cancelled}') # wait a moment await asyncio.sleep(0.1) # check the status of the task print(f'canceled: {task.cancelled()}') # report a final message print('main coroutine done') # start the asyncio program asyncio.run(main()) |
Running the example starts the asyncio event loop and executes the main() coroutine.
The main() coroutine reports a message, then creates and schedules the task coroutine.
It then suspends and waits a moment to allow the task coroutine to begin running.
The task runs, reports a message, and sleeps for a while.
The main() coroutine resumes and cancels the task. It reports that the request to cancel the task was successful.
It then sleeps for a moment to allow the task to respond to the request to be canceled.
The task_coroutine() resumes and a CancelledError exception is raised. The task catches the exception and reports a message. It could then continue running. In this case, there are no further operations to perform
The main() coroutine resumes and reports whether the task has the status of canceled.
In this case, the task is not marked as canceled. This is because the CancelledError exception did not unravel the coroutine, instead, it was caught and handled.
This example highlights that the cancel() method only requests a task cancel, it does not ensure that it will be canceled.
1 2 3 4 5 6 |
main coroutine started executing the task was canceled: True Received a request to cancel canceled: False main coroutine done |
Next, we will look at how to pass a message to a task when requesting it to cancel.
Example of a Canceling a Task With a Message
The cancel() method allows a message to be passed to the task that is being canceled.
This allows the caller to interact with the callee. For example, the string message could provide some indication of why the task must be canceled. The callee could catch the CancelledError exception and decide whether to cancel or not based on that information.
We can explore how to send a cancel message to a task via the cancel() method.
The example below updates the above example where the task handles the request to cancel and does not cancel. In this case, it reports the message that was passed by the caller.
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 28 29 30 31 32 33 34 35 |
# SuperFastPython.com # example of canceling a running task with a message import asyncio # define a coroutine for a task async def task_coroutine(): try: # report a message print('executing the task') # block for a moment await asyncio.sleep(1) except asyncio.CancelledError as e: print(f'received request to cancel with: {e}') # custom coroutine async def main(): # report a message print('main coroutine started') # create and schedule the task task = asyncio.create_task(task_coroutine()) # wait a moment await asyncio.sleep(0.1) # cancel the task was_cancelled = task.cancel('Stop Right Now') # report whether the cancel request was successful print(f'was canceled: {was_cancelled}') # wait a moment await asyncio.sleep(0.1) # check the status of the task print(f'canceled: {task.cancelled()}') # report a final message print('main coroutine done') # start the asyncio program asyncio.run(main()) |
Running the example starts the asyncio event loop and executes the main() coroutine.
The main() coroutine reports a message, then creates and schedules the task coroutine.
It then suspends and waits a moment to allow the task coroutine to begin running.
The task runs, reports a message, and sleeps for a while.
The main() coroutine resumes and cancels the task and passes it a unique message. It reports that the request to cancel the task was successful.
It then sleeps for a moment to allow the task to respond to the request to be canceled.
The task_coroutine() resumes and a CancelledError exception is raised. The task catches the exception and reports the exception. This includes the specific message provided by the caller in the request to cancel.
The main() coroutine resumes and reports whether the task has the status of canceled.
In this case, the task is not marked as canceled. This is because the CancelledError exception did not unravel the coroutine, instead, it was caught and handled.
This example highlights that the cancel() method only requests a task cancel, it does not ensure that it will be canceled.
1 2 3 4 5 6 |
main coroutine started executing the task was canceled: True received request to cancel with: Stop Right Now canceled: False main coroutine done |
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 how to cancel asyncio tasks in Python.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Joey Banks on Unsplash
Do you have any questions?