Channel Basics
Channels are Go's primary mechanism for goroutine communication. They're typed conduits — you can send and receive values of a specific type. Think of a channel as a pipe: one goroutine puts data in, another takes data out.
// Create a channel
ch := make(chan string)
// Send in a goroutine
go func() {
ch <- "Hello from goroutine!" // Send
}()
// Receive in main
msg := <-ch // Blocks until a value is available
fmt.Println(msg)
Hello from goroutine!
Unbuffered channels block: the sender waits until someone receives, and the receiver waits until someone sends. This provides synchronization without locks.
Buffered Channels
// Buffered channel: can hold 3 values without blocking
ch := make(chan int, 3)
ch <- 1 // Doesn't block (buffer has space)
ch <- 2
ch <- 3
// ch <- 4 // Would block! Buffer full
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
fmt.Println(len(ch), cap(ch)) // 1, 3
select — Multiplexing Channels
select waits on multiple channel operations simultaneously. It's like a switch for channels:
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(100 * time.Millisecond)
ch1 <- "from ch1"
}()
go func() {
time.Sleep(200 * time.Millisecond)
ch2 <- "from ch2"
}()
// Wait for whichever comes first
select {
case msg := <-ch1:
fmt.Println(msg)
case msg := <-ch2:
fmt.Println(msg)
case <-time.After(time.Second):
fmt.Println("timeout")
}
}
Common Patterns
// Close and range: producer signals "no more data"
func produce(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch) // Signal: no more values
}
ch := make(chan int)
go produce(ch)
// Range reads until channel is closed
for val := range ch {
fmt.Println(val) // 0, 1, 2, 3, 4
}
| Type | Syntax | Use Case |
|---|---|---|
| Bidirectional | chan T | General purpose |
| Send-only | chan<- T | Function parameters (producer) |
| Receive-only | <-chan T | Function parameters (consumer) |
⚠️ Common Mistake: Goroutine leak
A goroutine blocked on a channel send/receive that nobody will ever satisfy leaks forever. Always ensure channels are eventually read from, written to, or closed. Use context.Context for cancellation.
Blocks until receiver ready
Blocks when buffer full
Send-only or recv-only
Practice Exercises
Medium Build a Mini Project
Combine concepts from this tutorial to build a small utility or tool.
Medium Debug Challenge
Introduce a bug in one of the code examples and practice finding and fixing it.
Hard Refactoring Exercise
Rewrite one example using a different approach and compare the tradeoffs.