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.