Channels
Channels are typed conduits for sending and receiving values between goroutines — "Don't communicate by sharing memory; share memory by communicating." Unbuffered channels synch…
Introduction
Channels are typed conduits for sending and receiving values between goroutines — "Don't communicate by sharing memory; share memory by communicating." Unbuffered channels synchronize sender and receiver; buffered channels decouple them up to capacity.
Closing a channel signals no more values — receivers get zero value and ok=false. Sending on closed channel panics. Channels are the primary coordination primitive in Go alongside sync primitives.
Master channel direction (<-chan, chan<-), range-over-channel, and select for building robust concurrent pipelines.
The story
Uber's dispatch pipeline sends ride requests through channels to matching workers — producers enqueue, consumers dequeue, and backpressure naturally forms when matchers fall behind surge demand. Unbuffered channels synchronize handoff: the sender blocks until a worker receives, preventing unbounded memory growth during Black Friday traffic spikes.
Channels are typed conduits: chan Order carries order structs, not arbitrary interface{} values, keeping concurrent code type-safe.
Understanding the topic
Key concepts
- ch := make(chan int) unbuffered — send blocks until receive.
- ch := make(chan int, 10) buffered — send blocks when full.
- ch <- val send; val := <-ch receive.
- close(ch) signals done; receive returns (zero, false) when drained.
- Directional types: send-only chan<- int, receive-only <-chan int.
- range ch receives until channel closed.
sequenceDiagramProducer->>Channel: send valueChannel->>Consumer: receive valueConsumer->>Channel: ack / close
Step-by-step explanation
- Unbuffered: send and receive must meet — rendezvous synchronization.
- Buffered: send succeeds until buffer full, then blocks.
- Only sender should close — receiving side detects close via ok.
- Select multiplexes multiple channel operations.
- Nil channel blocks forever — used to disable select cases.
- Channel of struct{} for signal-only (no data payload).
Practical code example
Pipeline with unbuffered channel and graceful close:
package mainimport "fmt"func producer(out chan<- int) {for i := 1; i <= 5; i++ {out <- i}close(out)}func consumer(in <-chan int) {for val := range in {fmt.Printf("received %d\n", val)}fmt.Println("channel closed, consumer done")}func main() {ch := make(chan int)go producer(ch)consumer(ch)}
Output
received 1 received 2 ... channel closed, consumer done
Line-by-line code explanation
ch := make(chan Order)creates an unbuffered channel — synchronous handoff.ch <- ordersends a value — blocks until another goroutine receives.order := <-chreceives a value — blocks until a sender delivers.close(ch)signals no more sends — receivers drain remaining values then get zero value.v, ok := <-chdetects channel closure whenokis false.for v := range chloops until the channel is closed and drained.select(next lesson) multiplexes multiple channel operations.don't close from receiver side— only the sender closes; closing twice panics.
Key takeaway: Only producer closes. range loops until close. Directional channel types document intent in function signatures.
Real-world use
Where you'll use this in production
- Worker job distribution in parallel processors.
- Result aggregation from multiple API calls.
- Done signaling for graceful shutdown.
- Rate limiting with ticker channel and token bucket.
Best practices
- Only the sender closes the channel.
- Use buffered channels when producer/consumer speeds differ.
- Document ownership — who sends, who receives, who closes.
- Prefer context cancellation over arbitrary channel timeouts.
- Use directional channel types in function signatures.
Common mistakes
- Closing channel on receiver side — send panic.
- Sending on closed channel — panic.
- Forgetting close — range loops forever.
- Unbuffered channel causing deadlock with single goroutine.
- Sharing channel without clear ownership model.
Advanced interview questions
Q1BeginnerBuffered vs unbuffered channel?
Q2BeginnerWho closes a channel?
Q3IntermediateReceive from closed channel?
Q4IntermediateNil channel behavior?
Q5AdvancedImplement fan-in merging multiple channels.
Summary
Channels coordinate goroutines with typed send/receive. Unbuffered syncs; buffered decouples with capacity limit. Only sender closes; receivers detect via range or ok flag. Use directional types and clear ownership conventions. Next lesson: buffered channels — tuning backpressure.