Day 058 — Channels: unbuffered vs buffered¶
Month 3 · Week 1 · ⬅ Day 057 · Day 059 ➡ · Journal index
🎯 Learning Objective¶
Use channels to communicate between goroutines, and choose unbuffered vs buffered with intent.
📚 Topics¶
make(chan T)vsmake(chan T, n); send/receive blocking semantics- The rendezvous (synchronisation) property of unbuffered channels
close,rangeover a channel,len/cap
📖 Reading / Sources¶
- Tour of Go — Channels
- Tour of Go — Buffered Channels
- Effective Go — Channels
- Go spec — Channel types
📝 Notes¶
- "Don't communicate by sharing memory; share memory by communicating." Channels move ownership of data between goroutines → [[channels]].
- Unbuffered (
make(chan T), cap 0): a send blocks until a receiver is ready and vice-versa — they rendezvous. This is a synchronisation point, not just data transfer → [[unbuffered-channel]]. - Buffered (
make(chan T, n)): send blocks only when the buffer is full; receive blocks only when empty. It decouples producer and consumer by up tonitems → [[buffered-channel]]. len(ch)= items currently buffered;cap(ch)= buffer size. For unbuffered both reasoning points are 0.close(ch)signals "no more values." Receivers can keep draining buffered values after close; once drained, receives return the zero value withok == false.for v := range chexits when the channel is closed and drained → [[channel-axioms]].- Closing is the sender's job and must happen exactly once — see Day 059 for the axioms.
chan struct{}is the idiomatic zero-size signal channel (pure notification, no payload).
💻 Code Examples¶
nums := make(chan int)
go func() {
defer close(nums) // sender owns the close
for i := 0; i < 5; i++ {
nums <- i * i
}
}()
sum := 0
for n := range nums { // ends when nums is closed & drained
sum += n
}
fmt.Println(sum) // 30
Full code:
examples/month-03/channels/main.go· Run:go run ./examples/month-03/channels
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
| Channel pipeline: Gen → Map → Filter → Collect | ✅ | exercises/month-03/week-1/pipeline/ |
| Worker pool over a jobs channel | ✅ | exercises/month-03/week-1/workerpool/ |
🐛 Mistakes Made¶
- Sent on a buffered channel past its
capwith no receiver → blocked forever (deadlock). - Closed a channel from the receiver side; panicked when the sender later closed it again.
❓ Open Questions¶
- Is a buffered channel ever a substitute for a
WaitGroup? (Sometimes, e.g. a counting semaphore, but they express different intents.)
🧠 Active Recall (answer without looking)¶
-
Q: On an unbuffered channel, what happens when you send and no one is receiving?
A
The send blocks until some goroutine receives — the send and receive rendezvous at the same instant. -
Q: What does
v, ok := <-chgive afterchis closed and drained?
A
`v` is the element type's zero value and `ok` is `false`.
🪶 Feynman Reflection¶
An unbuffered channel is a direct handoff. Think of it as passing a baton: you can't let go until a teammate grabs it (rendezvous). A buffered channel is a short conveyor belt — you can drop a few items and walk away, until the belt fills up.
🕳️ Knowledge Gaps¶
- Performance trade-offs of buffer sizing under bursty load — measure later.
✅ Summary¶
I can move data between goroutines with channels and pick unbuffered (sync) vs buffered (decouple) deliberately, closing and ranging correctly.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 059: the precise channel axioms for
close,nil, and direction.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: feat(examples): unbuffered vs buffered channels (day 058)