When would you use Goroutine Vs Thread in a real project
Fast recall
Quick revision
Core answer
Practical understanding of Goroutine Vs Thread.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The model, in its three letters:
G — goroutine: the work item. Each G has its own stack and state, parked when blocked on channels/mutexes/timers.
M — machine: an OS thread. Ms are what actually execute — the runtime creates them as needed (and the OS caps them, hence the M is the scarce resource).
P — processor: the scheduling context that an M must hold to execute Go code. Each P has a local run queue of Gs ready to run. GOMAXPROCS sets the number of Ps — by default the CPU core count — which is why it bounds true parallelism: at most GOMAXPROCS Ms are executing Go code at once.
The dynamics that make the model work:
Work stealing: when a P's local queue empties, it steals Gs from another P's queue (half of a victim's queue) or checks global queues. This keeps all Ps busy without a central bottleneck.
Handoff on block: when a goroutine blocks on a channel, the runtime parks the G and the M (via its P) picks another G — the M never idles on a blocked G. When a goroutine enters a syscall, the M goes with it (the syscall owns the thread); the P is handed to another M so other Gs keep running. That's why blocking syscalls don't stall the machine in Go.
Preemption: since Go 1.14, the scheduler preempts long-running Gs at function calls (async preemption via signals) — no goroutine can hog a P forever, closing the loop for schedulability guarantees.
The number that anchors it: runtime.GOMAXPROCS(0) on a 16-core box prints 16 — 16 Ps, any number of Ms (grown on demand), and the hundreds of thousands of Gs distributed across the P queues.
runtime.GOMAXPROCS(0) // number of Ps — 16 on this box
runtime.NumGoroutine() // Gs alive right now
// M:N — the scheduler parks Gs on channel ops,
// hands Ps to other Ms during syscalls, steals work between PsWhen would you use Goroutine Vs Thread in a real project
Direct Answer
G = goroutine, M = machine (OS thread), P = processor (scheduler context, holds a run queue). The scheduler multiplexes Gs onto Ms through Ps — stealing work when a P runs dry.
Deep Dive & Architecture
Why three entities instead of two? Because the kernel thread (M) alone can't answer 'who runs next' — that's Go's job — and a global Go queue would serialize all scheduling on one lock. The P is the fix: a per-P local run queue means most scheduling decisions (push a new G, pop the next G) touch no shared lock at all — P-local, uncontended, fast. Only stealing and the global queue need coordination, and both are the cold path.
Walk the life of a G: created, pushed onto the current P's local queue. Some M holding that P pops and runs it. Three exits from 'running':
- Blocks on a channel: the G parks (into the channel's wait queue); the M picks the next G from its P. The M never left userspace scheduling.
- Blocks in a syscall: this G + its M dive into the kernel together. The runtime DETACHES the P — hands it to another M (from the runtime's spare-M pool, created on demand) so the rest of the P's queue keeps running. When the syscall returns, the original M re-acquires a P and continues.
- Yields / finishes: back to a queue; the M moves on.
Work stealing keeps the Ps balanced: a P with an empty local queue first checks the global queue (where long-waiting Gs go to prevent starvation), then steals half of some busy P's local queue. Net: no P starves while any other has work — the scheduler converges to full utilization without any central plan.
GOMAXPROCS bounds it: it's the number of Ps — the number of Ms simultaneously executing Go code — defaulting to the CPU count. One per core: the maximum useful parallelism; all the thousands of other Gs wait in queues, costing stacks, not cores.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Goroutine Vs Thread, and when would you use it?
- What is a common mistake when using Goroutine Vs Thread?
- How would you compare Goroutine Vs Thread with an alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.