Goroutines
What is a goroutine?
Section titled “What is a goroutine?”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.
Why goroutines are cheap
Section titled “Why goroutines are cheap”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 Go scheduler: M, P, G
Section titled “The Go scheduler: M, P, G”The scheduler operates on three concepts:
| Symbol | Meaning |
|---|---|
| G | Goroutine — a unit of concurrent work |
| M | Machine — an OS thread |
| P | Processor — 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.
Waiting with sync.WaitGroup
Section titled “Waiting with sync.WaitGroup”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 thegostatement)wg.Done()— decrement the counter by 1 (call withdeferinside 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")}Loading Go runtime (first run only, ~8 MB)…