Python: Add a coroutine to an already running event loop

Updated: August 10, 2023 By: Wolf Post a comment

This concise, exampled-base article walks you through some different ways to add a coroutine to an already running event loop in Python.

Using the asyncio.loop.create_task() method

This approach uses the loop.create_task() method, which creates a new task from the coroutine and schedules it to run on the loop. A task is a subclass of asyncio.Future that represents a coroutine that is executing or waiting to be executed. Tasks are used to track the progress and state of coroutines, and can also be awaited or cancelled.

Example:

# SlingAcademy.com
# This code uses Python 3.11.4

import asyncio


# define the coroutine
async def my_coroutine():
    # do something async
    await asyncio.sleep(2)

    print("Welcome to Sling Academy!")


# define the main function
async def main():
    # get the current event loop
    loop = asyncio.get_event_loop()

    # check if the event loop is already running
    if(loop.is_running()):
        print(f"Event loop is already running.")

    # create and schedule a new task
    task = loop.create_task(my_coroutine())

    # wait for the task to complete
    await task


# run the main function
asyncio.run(main())

When executing the code, you’ll receive the following messages (with a delay between):

Event loop is already running.
Welcome to Sling Academy!

Using the asyncio.ensure_future() function

The main point here is the asyncio.ensure_future() function, which is similar to loop.create_task() but also works with futures and other awaitable objects. A future is an object that represents an eventual result of an asynchronous operation. Futures can be awaited, cancelled, or have callbacks registered to them.

Example (the majority of the code is the same as in the previous example):

# SlingAcademy.com
# This code uses Python 3.11.4

import asyncio


# define the coroutine
async def my_coroutine():
    # do something async
    await asyncio.sleep(2)

    print("Welcome to Sling Academy!")


# define the main function
async def main():
    # get the current event loop
    loop = asyncio.get_event_loop()

    # check if the event loop is already running
    if loop.is_running():
        print(f"Event loop is already running.")

    # create and schedule a new future
    future = asyncio.ensure_future(my_coroutine())

    # wait for the future to complete
    await future


# run the main function
asyncio.run(main())

Using the asyncio.run_coroutine_threadsafe() function

An alternative solution is to utilize the asyncio.run_coroutine_threadsafe() function, which is helpful when you want to run a coroutine from a different thread than the one running the event loop. This function returns a concurrent.futures.Future object that can be used to wait for the result or cancel the execution.

Example:

# SlingAcademy.com
# This code uses Python 3.11.4

import asyncio
import threading


# Define a coroutine
async def add(a: int, b: int):
    # Mimic a long-running task
    print("The program is thinking of the result very hard...")
    await asyncio.sleep(1)

    # Calculate and return the result
    result = a + b
    return f"The sum of {a} and {b} is {result}"


# Define a function that runs the coroutine in a thread
def run_in_thread(loop, a: int, b: int):
    # Submit the coroutine to the loop
    future = asyncio.run_coroutine_threadsafe(add(a, b), loop)

    # Wait for the result
    print(f"Result: {future.result()}")


if __name__ == "__main__":
    # Get the main event loop
    loop = asyncio.get_event_loop()

    # Create a thread that runs the coroutine
    thread = threading.Thread(target=run_in_thread, args=(loop, 4, 5))

    # Start the thread
    thread.start()

    # Run the loop forever
    loop.run_forever()

Output:

The program is thinking of the result very hard...
Result: The sum of 4 and 5 is 9

This is an article dedicated to the asyncio.run_coroutine_threadsafe() function: Python asyncio.run_coroutine_threadsafe() function (with examples).