Skip to content

Goroutines

A goroutine is a function executing concurrently with other goroutines in the same address space. You launch one with a single keyword:

go f(args)

That is the entire syntax. go schedules f to run concurrently, then the calling goroutine proceeds immediately without waiting for f to return.

An OS thread typically starts with a 1–8 MB fixed stack. A goroutine starts with roughly 2–8 KB of stack that grows and shrinks automatically as needed (up to 1 GB by default). The Go runtime multiplexes goroutines onto OS threads using an M:N scheduler (M goroutines over N threads). Context switches between goroutines happen in user-space and are orders of magnitude cheaper than kernel thread switches.

In production, it is common to have tens of thousands of goroutines running simultaneously.

The scheduler operates on three concepts:

SymbolMeaning
GGoroutine — a unit of concurrent work
MMachine — an OS thread
PProcessor — a scheduling context (GOMAXPROCS of these exist)

Each P holds a run queue of Gs. An M must hold a P to run Gs. When a G blocks on I/O, its M releases the P so another M can pick it up — this is how the scheduler achieves concurrency without burning threads on blocked goroutines.

sync.WaitGroup is the standard way to wait for a fixed number of goroutines to finish.

  • wg.Add(n) — increment the counter by n (always before the go statement)
  • wg.Done() — decrement the counter by 1 (call with defer inside the goroutine)
  • wg.Wait() — block until the counter reaches zero
package main
import (
"fmt"
"sync"
)
func worker(id int, results []string, wg *sync.WaitGroup) {
defer wg.Done()
sum := 0
for j := 1; j <= 5; j++ {
sum += j
}
results[id-1] = fmt.Sprintf("worker %d: sum 1..5 = %d", id, sum)
}
func main() {
var wg sync.WaitGroup
results := make([]string, 3)
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, results, &wg)
}
wg.Wait()
for _, r := range results {
fmt.Println(r)
}
fmt.Println("all workers done")
}
What is the approximate initial stack size of a new goroutine?
Where must wg.Add(1) be called relative to the go statement?
What happens if main returns while goroutines are still running?
What does GOMAXPROCS control?