Concurrency: The Go Way
The Go concurrency model
Section titled “The Go concurrency model”Most languages bolt concurrency on top of shared-memory threads. Go was designed from the ground up around a different idea: Communicating Sequential Processes (CSP), formalized by Tony Hoare in 1978. The Go proverb that captures it best is:
Do not communicate by sharing memory; instead, share memory by communicating.
In practice this means:
- Goroutines are independently executing functions. They are cheap — a few kilobytes of stack that grows on demand — so you can have hundreds of thousands running concurrently.
- Channels are typed conduits. Goroutines send values into a channel and receive values from another, transferring both data and synchronization in one operation.
- The scheduler multiplexes goroutines onto a pool of OS threads (GOMAXPROCS threads by default, one per CPU core). You never manage threads directly.
This design makes concurrent programs easier to reason about. When data flows through channels, ownership is clear: the sender had it, now the receiver has it. No mutex needed for that transfer.
What this module covers
Section titled “What this module covers”| Lesson | Topic |
|---|---|
| This page | Model overview and first runnable example |
| Goroutines | go f(), the scheduler, sync.WaitGroup |
| Channels | Create, send, receive, buffered vs unbuffered, close, range |
| Select | Multiplexing channels, default, timeouts |
| Sync primitives | sync.Mutex, sync.Once, sync/atomic |
| Context | context.Context, cancellation, propagation |
| Patterns | Worker pool, fan-out/fan-in, pipeline |
Your first concurrent Go program
Section titled “Your first concurrent Go program”The snippet below launches three goroutines in parallel. Each goroutine writes its result into a pre-allocated slice indexed by its own id, so output is always in order. sync.WaitGroup blocks main until all three are finished.
package main
import ( "fmt" "sync")
func main() { var wg sync.WaitGroup results := make([]string, 3)
for i := 0; i < 3; i++ { wg.Add(1) go func(id int) { defer wg.Done() results[id] = fmt.Sprintf("goroutine %d says hello", id) }(i) }
wg.Wait() for _, r := range results { fmt.Println(r) } fmt.Println("main exits")}Loading Go runtime (first run only, ~8 MB)…