Goroutines & Channels
Go’s concurrency model follows one guiding principle: “Don’t communicate by sharing memory; share memory by communicating.” Goroutines do the work, channels pass data between them safely.
Goroutines
Section titled “Goroutines”A goroutine is a function running concurrently, started with go:
go doSomething()Goroutines are cheap — a few KB of stack that grows as needed — so it’s normal to start thousands of them. The catch: main doesn’t wait for goroutines to finish on its own, so you need a way to synchronize.
Channels
Section titled “Channels”A channel is a typed pipe for sending and receiving values between goroutines:
ch := make(chan string)
go func() { ch <- "done" // send}()
msg := <-ch // receive, blocks until a value arrivesfmt.Println(msg)chan<- string and <-chan string are directional channel types — a nice bit of documentation-by-signature showing a function only sends or only receives.
sync.WaitGroup
Section titled “sync.WaitGroup”When you don’t need to pass data back, just wait for goroutines to finish:
var wg sync.WaitGroup
for i := 0; i < 3; i++ { wg.Add(1) go func(id int) { defer wg.Done() fmt.Println("working", id) }(i)}
wg.Wait()select
Section titled “select”select waits on multiple channel operations at once, similar to a switch for channels:
select {case msg := <-ch1: fmt.Println("from ch1:", msg)case msg := <-ch2: fmt.Println("from ch2:", msg)case <-time.After(2 * time.Second): fmt.Println("timed out")}