Fundamentals 13 min read

Understanding Coroutines: From Ordinary Functions to User‑Level Threads

This article explains the concept of coroutines, compares them with ordinary functions, shows Python code examples of yielding and resuming execution, illustrates their historical background, and describes how they are implemented using heap‑allocated stack frames to achieve lightweight, user‑level concurrency.

High Availability Architecture
High Availability Architecture
High Availability Architecture
Understanding Coroutines: From Ordinary Functions to User‑Level Threads

As a programmer you may have heard the term coroutine , especially in high‑performance and high‑concurrency contexts, but you might not know exactly what it means. This article is written to give you a complete understanding of coroutines.

Ordinary function example

A simple function that prints three letters:

def func():
    print("a")
    print("b")
    print("c")

When called, the function runs from start to finish and then returns, producing the output:

a
b
c

From ordinary functions to coroutines

Unlike a normal function, a coroutine can have multiple return (or pause) points. In pseudo‑C syntax the function looks like:

void func() {
    print("a");
    // pause and return
    print("b");
    // pause and return
    print("c");
}

When the first pause is reached, control returns to the caller while the coroutine’s state is saved, allowing it to resume later from that point.

Python coroutine example

Using yield in Python turns the function into a coroutine:

def func():
    print("a")
    yield
    print("b")
    yield
    print("c")

It can be used as follows:

def A():
    co = func()          # obtain the coroutine object
    next(co)             # run until first yield (prints "a")
    print("in function A")
    next(co)             # resume, prints "b"

The output demonstrates that after the first next call the coroutine pauses, the surrounding function continues, and a subsequent next call resumes execution from the previous pause point.

Graphical explanation

Diagrams compare a normal function call stack with coroutine execution, showing how control returns to the caller after each yield and later resumes from the saved point.

Coroutines are a special case of functions

In essence, a coroutine is a function that can be suspended and later resumed; a regular function is simply a coroutine without any suspension points.

Historical background

The idea of coroutines dates back to 1958, predating threads. Early implementations appeared in Simula 67 and Scheme (1972). Their popularity waned with the advent of OS‑level threads, but they resurfaced in recent years due to the need for massive concurrency in modern services.

How coroutines are implemented

When a coroutine is paused, its execution context (the stack frame) must be saved. This is typically done by allocating the stack frame on the heap instead of the traditional call stack, allowing the runtime to store and restore the context without copying data back and forth.

Illustrations show multiple coroutines sharing a single OS thread, each with its own heap‑allocated stack, enabling thousands of concurrent execution flows with minimal overhead.

Conclusion

Coroutines provide user‑level threading: they allow many lightweight execution flows, preserve their state across pauses, and give programmers explicit control over scheduling, making them ideal for high‑concurrency applications.

Pythonconcurrencyprogramming fundamentalscoroutineyielduser-level threads
High Availability Architecture
Written by

High Availability Architecture

Official account for High Availability Architecture.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.