Introduction
Channels are Go's built-in mechanism for goroutines to communicate and synchronize. Instead of sharing memory and protecting it with locks, Go encourages passing data through channels: "Don't communicate by sharing memory; share memory by communicating." A channel is a typed pipe you can send values into and receive values from, and by default this send/receive is synchronous, which naturally coordinates goroutines without extra locking.
Cricket analogy: Instead of two batsmen shouting across the pitch and risking a mix-up, they run between the wickets using a clear call-and-response, just as goroutines use a channel's send/receive to synchronize instead of sharing a scoreboard directly.
Syntax
ch := make(chan int) // unbuffered channel
bch := make(chan int, 5) // buffered channel with capacity 5
ch <- 42 // send a value into the channel
v := <-ch // receive a value from the channel
close(ch) // close the channel; no more sends allowed
for v := range ch {
// receives values until the channel is closed
}Explanation
An unbuffered channel (make(chan int)) has no capacity: a send blocks until another goroutine is ready to receive, and vice versa. This makes unbuffered channels a synchronization point as well as a data pipe. A buffered channel (make(chan int, 5)) can hold up to 5 values without a receiver present; sends only block once the buffer is full, and receives block only when it's empty. Closing a channel with close(ch) signals that no more values will be sent; receivers can keep draining any buffered values, and for v := range ch automatically stops once the channel is closed and drained. Sending on a closed channel causes a panic, so only the sender should close a channel.
Cricket analogy: An unbuffered channel is like a strict single-run between wickets where the striker and non-striker must both commit at once; a buffered channel of 5 is like a bowler who can stack up to 5 deliveries in an over before the umpire calls dead ball, and close(ch) is the umpire signaling declaration so range stops reading the scorecard once the innings ends.
Example
package main
import "fmt"
func produce(ch chan<- int, count int) {
for i := 1; i <= count; i++ {
ch <- i * i
}
close(ch)
}
func main() {
ch := make(chan int, 3)
go produce(ch, 5)
for v := range ch {
fmt.Println("received:", v)
}
fmt.Println("channel closed, done")
}Output
received: 1
received: 4
received: 9
received: 16
received: 25
channel closed, done
Because the producer closes the channel after sending all values, range exits cleanly once every buffered value has been received.
Cricket analogy: The clean sequence of received squares (1, 4, 9, 16, 25) followed by a closing message is like a scorer logging each ball's total before the umpire calls stumps, so the tally sheet closes only after every run is recorded.
Key Takeaways
- Channels are typed pipes for sending and receiving values between goroutines.
- Unbuffered channels (
make(chan T)) synchronize sender and receiver; sends block until received. - Buffered channels (
make(chan T, n)) allow up to n values to queue before a send blocks. close(ch)signals no more values will be sent; only the sender should close a channel.for v := range chreads values until the channel is closed and drained.- Go favors sharing memory by communicating over channels rather than shared variables and locks.
Practice what you learned
1. What does `make(chan int)` create?
2. What happens when you send on a channel that has been closed?
3. In a buffered channel created with `make(chan int, 5)`, when does a send operation block?
4. What is the core Go concurrency philosophy channels are built around?
Was this page helpful?
You May Also Like
Goroutines in Go
Goroutines are lightweight, runtime-managed threads that let Go programs run functions concurrently with minimal overhead.
The select Statement in Go
The select statement lets a goroutine wait on multiple channel operations at once, proceeding with whichever is ready first.
Worker Pools in Go
A worker pool spawns a fixed number of goroutines that consume jobs from a channel, an idiomatic pattern for bounded concurrent processing.
The sync Package in Go
The sync package provides primitives like WaitGroup, Mutex, and Once for coordinating goroutines and protecting shared state.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics