Coroutine

Although coroutines may feel like a relatively new technology, they have actually been around since 1958, before the concept of threads even appeared. In the days before threads, programmers had no choice but to use coroutines to write concurrent programs. Later, as threads emerged and operating systems began to support concurrent program execution by default, coroutines were forgotten.

However, with the recent development of the Internet, especially in the mobile Internet era, user requests that must be processed by servers have begun to increase exponentially. In this situation, coroutines found their place in fields that required high performance and concurrency.

Just a special example of functions.

There is no formal difference between a coroutine and a regular function. However, a coroutine has the ability to suspend and resume, which are very similar to threads. In other words, what makes a coroutine different from a regular function is that it can know where it was last executed.

Computer systems periodically generate timer interrupts, and each time an interrupt is processed, the operating system decides whether to suspend the current thread. Therefore, the programmer does not need to explicitly specify when to suspend the thread and give up CPU resources. However, since there is no way to operate a timer interrupt in user mode, a coroutine must explicitly specify where to suspend and give up CPU resources by using a reserved word such as yield.

It is important to note that the operating system does not know how many coroutines are created. Since a coroutine is implemented entirely within user mode, it can be interpreted as a user mode thread.

Implementation

The implementation of a coroutine is actually not fundamentally different from the implementation of a thread. Since a coroutine can be suspended or resumed, it is necessary to record context information when it is suspended. Since a coroutine is also a kind of function, the stack frame containing this context information must be stored somewhere. But, it is stored in the heap, not the stack.

In theory, there is no limit to the number of coroutines as long as there is enough memory space, and switching or scheduling between coroutines happens entirely in user mode, so there is no need for the operating system to intervene. It is also much more efficient because the information that is saved or restored when switching between coroutines is lighter.

How to work

Coroutines can achieve the same effects as asynchronous execution even when programmed synchronously. Also, the worker thread is not blocked even if the coroutine is suspended.

Synchronous and asynchronous methods are similar to telephone and email.

  • Synchronous
    • When talking on the phone, one person speaks and the other listens. When A speaks, B must wait, and only after A finishes speaking can B continue speaking.
  • Asynchronous
    • There is no need to wait when communicating via email. While A is writing an email, B can do other things. Likewise, after A has finished writing and sending an email, he can do other things while waiting for B to reply.

Meanwhile, there are two confusing but similar concepts: blocking and non-blocking. For example, when function A calls function B, if the operating system suspends the thread or process where function A is running at the same time as calling function B, the call method for function B is blocking, otherwise it is non-blocking. This is similar to ordering a pizza.

  • Blocking
    • It’s similar to going to a pizza place and ordering pizza in person. In this case, it is like waiting inside the store until the pizza is ready.
  • Non-blocking
    • It’s similar to ordering pizza over the phone. In this case, it is like doing something else until the delivery arrives, rather than waiting endlessly for the pizza at the front door.

When a coroutine is suspended, the worker thread switches to execute another coroutine that is ready, and when the suspended coroutine resumes and returns its processing result, it becomes ready again and waits for its turn to be scheduled.

Threads are generally called kernel mode threads. On the other hand, coroutines are elements unknown to the kernel, and regardless of how many coroutines are created, the kernel allocates CPU time to each thread. The programmer can decide which coroutine to execute within the time allocated to the thread. Therefore, coroutines are also called user mode threads.

Reference

[1] 루 샤오펑. 2024. 컴퓨터 밑바닥의 비밀, 길벗


© 2025. All rights reserved.