Knowledge Guide
HomeConcurrencyConcurrency Foundations

Go Concurrency — Goroutines, Channels & the CSP Model

A different model: communicate, don't share

Everything so far — locks, atomics, the memory model — is the shared-memory model: many threads touch the same variable, and you guard it. Go takes the opposite stance, CSP (Communicating Sequential Processes):

"Do not communicate by sharing memory; instead, share memory by communicating." — Go proverb

Instead of locking a shared value, you give ownership of data to one goroutine and pass it to others over a channel. The race can't happen because only one goroutine touches the data at a time. Same problems (visibility, races, deadlock) — different default tool.

Goroutines: threads, but cheap

A goroutine is a function scheduled by the Go runtime onto a small pool of OS threads. They start at ~2 KB of stack and grow on demand, so a program can run hundreds of thousands — where OS threads top out in the thousands. You launch one with go, and wait with a WaitGroup:

var wg sync.WaitGroup
for i := 0; i < 3; i++ {
    wg.Add(1)
    go func(id int) { defer wg.Done(); work(id) }(i)  // runs concurrently
}
wg.Wait()  // block until all three Done()

The same race — and Go's killer feature, the race detector

CSP is a discipline, not a guarantee: share a variable across goroutines and you get the exact race from the first lesson.

counter := 0
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
    wg.Add(1)
    go func() { defer wg.Done(); counter++ }()   // DATA RACE
}
wg.Wait()
fmt.Println(counter)   // < 1000, varies

What Go gives you that the JVM doesn't out of the box: go run -race instruments memory access and prints the exact two stacks that raced. Run it in CI and races stop being heisenbugs. The three fixes:

// 1. Mutex (shared-memory style)
var mu sync.Mutex
mu.Lock(); counter++; mu.Unlock()

// 2. Atomic
var counter int64
atomic.AddInt64(&counter, 1)

// 3. CSP style: one goroutine owns the counter; others send deltas on a channel
inc := make(chan int)
go func() { for d := range inc { counter += d } }()   // sole owner — no lock needed
inc <- 1
Unbuffered channel is a synchronous handoff; buffered channel is a bounded queue that blocks only when full
Unbuffered channel is a synchronous handoff; buffered channel is a bounded queue that blocks only when full

Channels: typed pipes between goroutines

A channel carries values of one type between goroutines. Unbuffered (make(chan T)) is a synchronous handoff — the send blocks until someone receives, so it doubles as synchronization. Buffered (make(chan T, n)) is a bounded queue — the send blocks only when full (natural back-pressure).

ch := make(chan int)        // unbuffered
go func() { ch <- 42 }()    // send (blocks until received)
v := <-ch                   // receive  -> 42
A producer feeds a jobs channel; three worker goroutines pull jobs and push to a results channel
A producer feeds a jobs channel; three worker goroutines pull jobs and push to a results channel

The worker pool — Go's answer to a thread pool

No ExecutorService: you just start N goroutines that range over a shared jobs channel and push to a results channel. The channel is the queue and the synchronization.

jobs := make(chan int, 100)
results := make(chan int, 100)
for w := 0; w < 3; w++ {                       // 3 workers
    go func() { for j := range jobs { results <- process(j) } }()
}
for _, t := range tasks { jobs <- t }
close(jobs)                                     // sender closes -> workers' range ends
for range tasks { total += <-results }          // collect

select — wait on many channels, with timeout and cancellation

select {
case v := <-ch:                 use(v)
case <-time.After(time.Second): timeout()       // no message in 1s
case <-ctx.Done():              return ctx.Err() // cancellation/deadline
}

select is the CSP analogue of condition variables: instead of wait/notify on a monitor, you block on whichever channel becomes ready first.

Java ↔ Go, side by side

ConceptJava (shared memory)Go (CSP)
Unit of concurrencyThread / Runnablegoroutine (go f())
Wait for completionjoin() / Futuresync.WaitGroup / channel
Mutual exclusionsynchronized / ReentrantLocksync.Mutex
Atomic counterAtomicIntegersync/atomic
Signaling / waitingwait() / notify()channels + select
Thread poolExecutorServicegoroutines over a jobs channel
Visibilityvolatile / happens-beforechannel send/recv establishes happens-before
Default mindsetshare state, guard with lockspass ownership, communicate

Pitfalls

Takeaways


Re-authored for this guide; worker-pool and channel diagrams hand-authored as SVG. Follows Go's "Share Memory By Communicating" blog post, Rob Pike's CSP talks, and the Go memory model. Cross-links: Race Conditions, Memory Model & Visibility, Atomics & CAS.

🤖 Don't fully get this? Learn it with Claude

Stuck on Go Concurrency — Goroutines, Channels & the CSP Model? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Go Concurrency — Goroutines, Channels & the CSP Model** (Concurrency) and want to truly understand it. Explain Go Concurrency — Goroutines, Channels & the CSP Model from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Go Concurrency — Goroutines, Channels & the CSP Model** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Go Concurrency — Goroutines, Channels & the CSP Model** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Go Concurrency — Goroutines, Channels & the CSP Model** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes