How to Merge Channels in Go: Simple Guide with Examples
To merge multiple
chan channels in Go, create a new channel and use a goroutine with a select statement to forward values from all input channels to the new one. This pattern lets you combine multiple streams of data into a single channel for easier processing.Syntax
To merge channels, you typically define a function that takes multiple input channels and returns a single output channel. Inside, you use a select statement inside a goroutine to listen to all input channels and send received values to the output channel.
Key parts:
chan T: channel type carrying values of typeTselect: waits on multiple channel operationsgoroutine: runs concurrent code to forward values
go
func merge(channels ...<-chan int) <-chan int { out := make(chan int) go func() { defer close(out) for { select { case v, ok := <-channels[0]: if ok { out <- v } else { return } case v, ok := <-channels[1]: if ok { out <- v } else { return } // Add more cases for more channels } } }() return out }
Example
This example merges two channels that send numbers. The merged channel outputs all numbers from both channels as they arrive.
go
package main import ( "fmt" "time" ) func merge(channels ...<-chan int) <-chan int { out := make(chan int) go func() { defer close(out) for { select { case v, ok := <-channels[0]: if ok { out <- v } else { return } case v, ok := <-channels[1]: if ok { out <- v } else { return } } } }() return out } func main() { ch1 := make(chan int) ch2 := make(chan int) go func() { for i := 1; i <= 3; i++ { ch1 <- i time.Sleep(100 * time.Millisecond) } close(ch1) }() go func() { for i := 10; i <= 12; i++ { ch2 <- i time.Sleep(150 * time.Millisecond) } close(ch2) }() merged := merge(ch1, ch2) for v := range merged { fmt.Println(v) } }
Output
1
10
2
3
11
12
Common Pitfalls
Common mistakes when merging channels include:
- Not closing the output channel, which can cause deadlocks.
- Not handling closed input channels properly, leading to infinite loops or panics.
- Using a fixed
selectwith a fixed number of cases, which is not scalable for many channels.
To fix these, use a sync.WaitGroup or dynamic goroutines to handle any number of channels and close the output channel only after all inputs are done.
go
package main import ( "fmt" "sync" ) func merge(channels ...<-chan int) <-chan int { out := make(chan int) var wg sync.WaitGroup output := func(c <-chan int) { defer wg.Done() for v := range c { out <- v } } wg.Add(len(channels)) for _, c := range channels { go output(c) } go func() { wg.Wait() close(out) }() return out } func main() { ch1 := make(chan int) ch2 := make(chan int) go func() { ch1 <- 1 ch1 <- 2 close(ch1) }() go func() { ch2 <- 10 ch2 <- 20 close(ch2) }() for v := range merge(ch1, ch2) { fmt.Println(v) } }
Output
1
2
10
20
Quick Reference
Tips for merging channels in Go:
- Use a goroutine per input channel to forward values.
- Use
sync.WaitGroupto wait for all goroutines to finish. - Close the output channel only after all inputs are done.
- Use
rangeto read from channels until they close.
Key Takeaways
Merge channels by forwarding values from each input channel to a single output channel using goroutines.
Always close the output channel after all input channels are closed to avoid deadlocks.
Use sync.WaitGroup to handle any number of input channels cleanly and safely.
Avoid fixed select statements for many channels; use a goroutine per channel instead.
Reading from closed channels with range helps simplify channel merging logic.