Patterns: concurrent Go · 3 MIN READ
Fan-out and fan-in: merge without assuming order
Coordinate multiple producers and one result stream.
Coordinate multiple producers and one result stream. Fan-out spreads work across producers or workers; fan-in merges their results. Trace the worked Go example, then compare the benefit with the cost before applying this design to your own project.
Concurrent Go pattern · A MAP OF THE IDEA
- Several sources produce independently
- Forwarders merge their values
- Coordinator closes one shared output
Useful when
- Independent sources should overlap
- One consumer should observe combined results
Watch out
- Early exit can block forwarding goroutines
- Ordered output needs extra identity and buffering
Implementation & operation cost: Medium
Fit depends on the task
Editorial suitability, not benchmarks or a universal ranking. Styles describe different axes and can be combined.
The design decision
Fan-out spreads work across producers or workers; fan-in merges their results. The merger must know when all producers are done before closing the output. Arrival order generally differs from input order, so retain sequence numbers if the product needs ordered results.
A complete Go example
Read this program from the entry point and follow the calls shown in the diagram. The output is included at the end so you can check the behavior without a compiler.
package main
import (
"fmt"
"sync"
)
func Merge(inputs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, input := range inputs {
wg.Add(1)
go func(ch <-chan int) {
defer wg.Done()
for n := range ch {
out <- n
}
}(input)
}
go func() { wg.Wait(); close(out) }()
return out
}
func main() {
a, b := make(chan int, 2), make(chan int, 2)
a <- 1
a <- 2
close(a)
b <- 3
b <- 4
close(b)
sum := 0
for n := range Merge(a, b) {
sum += n
}
fmt.Println(sum)
}
// Output:
// 10
Each forwarding goroutine signals completion, and only the coordinator closes out. main consumes the full merge, so every producer can finish. The sum is deterministic; the individual values can arrive in several valid orders.
Tradeoffs and the simpler alternative
When a consumer may stop early, use context-aware sends and ensure upstream producers also stop. Merely buffering out to a guessed size hides the lifecycle problem. A simple sequential loop is easier when there is no parallel work to overlap.
TAKE THIS WITH YOU
Closes merged output after every producer has finished. When a consumer may stop early, use context-aware sends and ensure upstream producers also stop. Merely buffering out to a guessed size hides the lifecycle problem. A simple sequential loop is easier when there is no parallel work to overlap.
Check your understanding
Which statement best describes Fan-out and fan-in: merge without assuming order?
Go to the source
Progress stays in this browser. No account needed.