Comparison of Channel Iteration in Go
Let’s compare different methods for reading from channels in Go.
· 3 min read
Donation Goal Tracker #
Below is a Go program that tracks donations using channels. Two goroutines (f(10) and f(15)) listen for updates on the donation balance, and another goroutine increments the balance every second.
package main
import (
"fmt"
"time"
)
type Donation struct {
balance int
ch chan int
}
func main() {
donation := &Donation{ch: make(chan int)}
// Listener Goroutine (Checks if goal amount is reached)
f := func(goal int) {
for balance := range donation.ch {
if balance >= goal {
fmt.Printf("%d goal reached\n", balance)
return
}
}
}
go f(10)
go f(15)
// Updater Goroutine (Increases balance)
go func() {
for {
time.Sleep(time.Second)
donation.balance++
donation.ch <- donation.balance
}
}()
time.Sleep(25 * time.Second) // Keep the program running
}Using for range to Read from a Channel #
This is same as the previous example, but written in a more concise way.
for balance := range donation.ch {
if balance >= goal {
fmt.Printf("%d goal reached\n", balance)
return
}
}How for range Works #
range donation.ch: Iterates over incoming values from the channel.- Each time a new value is sent to
donation.ch, it is immediately read and assigned tobalance. - The loop continues waiting for new values until the channel is closed.
Key Characteristics of for range #
- Automatically waits for new values.
- Stops when the channel is closed.
- Simple and efficient for single-channel reading.
Using select to Listen for Channel Data #
f := func(goal int) {
for {
select {
case balance := <-donation.ch: // Process incoming value
if balance >= goal {
fmt.Printf("%d goal reached\n", balance)
return
}
}
}
}How select Works #
case balance := <-donation.ch: Reads from the channel only when data is available.- Useful when monitoring multiple channels simultaneously.
- Requires an explicit return or break to exit the loop.
Key Characteristics of select #
- Can handle multiple channels.
- Only executes when a channel has data.
- Requires an explicit exit condition.
Why switch is Not Used #
A switch statement is not suitable for continuously reading from a channel.
for {
balance := <-donation.ch
switch {
case balance >= goal:
fmt.Printf("%d goal reached\n", balance)
return
default:
}
}Problems with Using switch #
balance := <-donation.ch: Directly blocks waiting for a value (inefficient).default: Executes unconditionally when no case matches, which prevents proper subscription behavior.
Key Characteristics of switch #
- Cannot wait for channel updates.
- Inefficient because it forces direct value retrieval.
defaultruns even when there’s no new data.
Comparison Table: for range vs. select vs. switch #
| Method | How It Works | Can Monitor Channels? | Exit Condition |
|---|---|---|---|
for range | Iterates when new values arrive | Yes | Automatically stops when the channel is closed |
select | Handles multiple channels | Yes | Requires explicit break or return |
switch | Simple conditional statement | No | Must manually read values |
Conclusion #
- Use
for rangewhen working with a single channel that continuously receives values. - Use
selectwhen handling multiple channels or managing timeouts. - Avoid
switchfor channel reading, as it lacks proper subscription behavior.