Sobes.tech
Middle

Are coroutines executed in a single thread or multiple?

sobes.tech AI

Answer from AI

Coroutines are inherently executed in a single thread. They implement cooperative multitasking, where switching between tasks occurs explicitly when a coroutine "yields" control using the await keyword. Unlike threads, where the operating system manages switching, in coroutines, the program does.

However, coroutines can use thread or process pools to perform blocking operations (such as input/output) so as not to block the main event loop. In such cases, the blocking operation itself runs in another thread/process, but waiting for its result occurs in the same thread where the event loop with coroutines operates.

Key differences:

Feature Threads Coroutines
Management Operating system (preemptive) Program (cooperative)
Parallelism True parallelism (on multi-core) Concurrency (single thread)
Switching Expensive (OS context switch) Cheap (control transfer)
Usage Blocking operations, CPU-bound tasks Non-blocking operations, I/O-bound tasks
Creation Expensive Cheap
Memory Larger Smaller

Example of an asynchronous function using asyncio:

import asyncio

async def fetch_data(delay):
    # Simulate an asynchronous operation
    print(f"Starting to fetch data (delay {delay} sec)")
    await asyncio.sleep(delay)
    print(f"Data fetched after {delay} sec")
    return f"Data after {delay} sec"

async def main():
    # Launch multiple coroutines
    task1 = asyncio.create_task(fetch_data(2))
    task2 = asyncio.create_task(fetch_data(1))

    # Wait for coroutines to complete
    result1 = await task1
    result2 = await task2

    print(f"Results: {result1}, {result2}")

if __name__ == "__main__":
    # Run the main coroutine in the event loop
    asyncio.run(main())

In this example, fetch_data is a coroutine. await asyncio.sleep(delay) hands control over to the event loop, allowing other tasks (in this case, another fetch_data coroutine with a 1-second delay) to run in the same thread while waiting for the "sleep" to complete.

Thus, coroutines do not create new threads by themselves, but they are excellent for efficiently managing task concurrency within a single thread, especially for I/O-bound operations where CPU usage is not intensive.