Golang / GoLang Concurrency Mastery Interview Questions
Explain Go's GMP scheduler model. What are M, P, and G and how do they interact?
Go uses an M:N scheduler — M goroutines multiplexed onto N OS threads, managed by the Go runtime. The three key entities are:
| Entity | Symbol | Description |
|---|---|---|
| Goroutine | G | Lightweight user-space thread with its own 2 KB stack. Contains the goroutine's code, stack pointer, and scheduling state. |
| Machine (OS thread) | M | A real kernel thread that executes Go code. The number of active Ms equals GOMAXPROCS by default. |
| Processor (logical CPU) | P | A scheduling context holding a local run queue of runnable Gs. One P per logical CPU (controlled by GOMAXPROCS). An M must hold a P to execute Go code. |
import "runtime" // GOMAXPROCS sets the number of Ps (default = runtime.NumCPU()) runtime.GOMAXPROCS(4) // 4 Ps â up to 4 goroutines running in true parallel // Query without changing numP := runtime.GOMAXPROCS(0) fmt.Println("Ps:", numP) // Each P holds a local run queue: a lock-free ring buffer of ~256 G slots // There is also a global run queue for overflow // Work stealing: when a P's local queue is empty, // it steals half the Gs from another P's queue // This keeps all CPUs busy without a central scheduling lock
Goroutine lifecycle through GMP: (1) go func() creates a G and places it in the current P's local run queue. (2) The M bound to that P picks up the G and executes it. (3) If the G makes a blocking syscall, the M detaches from its P; the P binds to a new M and continues running other Gs. When the syscall returns, the original M tries to reacquire a P — if none is free, the G goes to the global queue and M parks. (4) Asynchronous preemption (Go 1.14+): a goroutine running too long without a function call receives a SIGURG signal and is preempted.
More Related questions...