Questions: Thread Models: User-Level and Kernel Threads
5 questions to test your understanding
Score: 0 / 5
Question 1 Multiple Choice
A server uses user-level threads to handle 10 simultaneous client connections. One thread makes a blocking disk read. What happens to the other 9 threads?
AThey continue running on other CPU cores, since the user-level thread library schedules them independently
BThey all block, because the OS sees only one process and suspends it entirely while waiting for the disk
CThe OS moves them to a different process so they can continue running
DThey automatically migrate to kernel threads to bypass the blocking issue
This is the critical flaw of user-level threads: the OS doesn't know they exist. When one user-level thread makes a blocking system call, the OS blocks the entire process — it cannot know other threads want to run because they are invisible to the kernel. The user-space library has no way to intercept a blocking kernel call and schedule another thread instead. This makes user-level threads unsuitable for I/O-bound applications despite their low scheduling overhead.
Question 2 Multiple Choice
A developer benchmarks thread creation and finds that creating 10,000 kernel threads (1:1 model) is significantly slower than creating an equivalent number of goroutines in Go. What is the most likely explanation?
AUser-space scheduling introduces quadratic overhead when threads number in the thousands
BEach kernel thread creation requires a system call and kernel data structure allocation, which accumulates at scale; Go goroutines are multiplexed onto far fewer kernel threads
CThe 1:1 model cannot support more than a few hundred threads on any OS
DThread creation is slower in languages without garbage collection
In a 1:1 model, each thread requires a system call, a kernel thread data structure, and a dedicated kernel stack. With 10,000 threads, this is 10,000 system calls plus kernel memory allocations. Go's goroutines implement an M:N model: thousands of goroutines are multiplexed onto a much smaller number of kernel threads (typically one per CPU core). Goroutine creation is a user-space operation costing microseconds, while kernel thread creation costs tens to hundreds of microseconds. Go's runtime scheduler handles the multiplexing efficiently.
Question 3 True / False
With user-level threads, four threads in the same process can achieve true parallelism across a four-core CPU.
TTrue
FFalse
Answer: False
User-level threads are invisible to the kernel scheduler, which schedules processes (or kernel threads), not user threads. The OS assigns one CPU slot to the process, and the user-space library multiplexes its threads onto that one slot — achieving concurrency (interleaving) but not parallelism (simultaneous execution on multiple cores). To use multiple cores, threads must be visible to the OS, either as kernel threads (1:1) or the kernel-thread component of an M:N hybrid.
Question 4 True / False
Kernel threads are slower to create and switch than user-level threads because all kernel thread operations require crossing the user-kernel boundary via a system call.
TTrue
FFalse
Answer: True
A system call involves saving user-space context, switching the CPU to kernel mode (a privileged mode change), executing the kernel operation, then returning — typically tens of microseconds. User-space thread context switches just swap register sets without any privilege level change, costing microseconds or less. This overhead difference motivates M:N threading approaches in languages like Go and Erlang, where the runtime multiplexes millions of lightweight threads onto a small fixed pool of kernel threads.
Question 5 Short Answer
What is the fundamental tradeoff between user-level and kernel-level threads, and why have most modern operating systems settled on the 1:1 model?
Think about your answer, then reveal below.
Model answer: User-level threads offer fast creation and context switching (no system calls) but sacrifice parallelism and block the whole process on any blocking syscall. Kernel threads enable true parallelism and correct blocking behavior but incur syscall overhead. Modern systems use 1:1 because: hardware has gotten faster (making syscall overhead less significant), multicore CPUs make parallelism highly valuable, and 1:1 is far simpler to reason about than M:N, avoiding the scheduling conflicts and priority inversions that made M:N models difficult to implement correctly.
The M:N model seemed theoretically ideal but proved too complex in practice — the user-space scheduler and kernel scheduler make independent decisions that can interfere. Go's goroutine runtime is a successful modern exception, but it works because Go controls the entire runtime and can coordinate the two schedulers. For general-purpose OS threading, 1:1 with efficient kernel operations has won out. The modest overhead of a system call is an acceptable price for correctness and predictable parallelism.