A scheduler selects runnable execution units and assigns processing resources. In Go, the runtime schedules goroutines (G) onto OS threads (M), while the OS schedules those threads onto CPU cores (P). GOMAXPROCS limits the number of Ms executing user-level code simultaneously, although blocked system calls can cause the runtime to create more Ms. section 8.2.1
Avoid assuming that creating several goroutines immediately uses several cores:
- Classify goroutines as executing, runnable, or waiting; waiting can result from system calls or synchronization operations. section 8.2.1
- Expect runnable goroutines to wait in a local queue associated with a P or in the global runnable queue. section 8.2.1
- Account for work stealing: an idle P may take goroutines from another P, but the timing and final thread assignment are not guaranteed. section 8.5
- Match worker-pool sizing to CPU-bound work and GOMAXPROCS rather than expecting runtime.NumCPU alone to determine execution. section 8.5
A fixed worker pool does not guarantee immediate parallelism
A pool can start several goroutines that receive jobs from one channel, wait for them with a sync.WaitGroup, and size the pool from runtime.GOMAXPROCS(0). Initially, however, multiple workers may remain runnable while only one executes; later, work stealing and OS thread placement can distribute them across cores. Treat that distribution as an eventual scheduling outcome, not a controllable layout. section 8.5
runtime.schedule() {
// Only 1/61 of the time, check the global runnable queue for a G.
// If not found, check the local queue.
// If not found,
// Try to steal from other Ps.
// If not, check the global runnable queue.
// If not found, poll network.
}
Inspect the scheduler’s simplified selection order: sample the global queue, consult a local queue, attempt work stealing, recheck globally, and poll the network. section 8.2.1











