Threads & Concurrency Models

user-level threads

Imagine a manager who keeps a private to-do list and quietly switches between jobs at their own desk, without ever telling the head office which task they are working on. The head office sees one busy employee and never gets involved in the switching. User-level threads are like that: threads created and managed entirely by a library inside your program, in user space, with the operating-system kernel knowing nothing about them. As far as the kernel is concerned, the whole process is just one thread of execution.

Concretely, a user-level thread library lives in your program and keeps its own table of threads, each with its own saved stack and registers. When one thread should yield to another, the library does the switch itself, in user mode, by saving one thread's registers and loading the next — no system call, no entry into the kernel. Because all of this happens without a kernel round-trip, creating a thread and switching between threads is extremely fast and cheap. The library, not the OS scheduler, decides which user thread runs next, so the program can use a scheduling policy tailored to itself.

The trade-off is the catch every beginner must learn. Since the kernel sees only one schedulable entity, two things follow. First, if any one user thread makes a blocking system call (say, reading from a slow disk), the kernel blocks the whole process — every other user thread is frozen too, because the kernel does not know they exist to schedule something else. Second, the process can run on only one CPU core at a time, so user-level threads alone give you concurrency but not true parallelism. These limits are exactly why pure user-level threading is usually paired with kernel support (the many-to-many model) or replaced by one-to-one threads when real parallelism matters.

An old cooperative threading library lets you spawn thousands of user-level threads almost for free and switch among them in nanoseconds. But the day one of them calls a blocking read() on a slow file, all the others stop dead until that read returns — the kernel parked the whole process, having no idea the rest were waiting to run.

Fast and cheap, but invisible to the kernel — one blocking call freezes them all.

User-level threads alone cannot use multiple cores, and one blocking system call can stall every thread in the process. Their speed comes precisely from staying out of the kernel.

Also called
user threadsuser-space threads使用者執行緒使用者空間執行緒