Fan-out: distribute work across multiple goroutines. Fan-in: collect results into one channel.
2 · Worked example — read every step
// Fan-out: launch N workers reading from the same input channel
func fanOut(input <-chan string, workers int) []<-chan Result {
channels := make([]<-chan Result, workers)
for i := 0; i < workers; i++ {
channels[i] = worker(input)
}
return channels
}
func worker(input <-chan string) <-chan Result {
out := make(chan Result)
go func() {
defer close(out)
for item := range input {
out <- process(item)
}
}()
return out
}
// Fan-in: merge multiple result channels into one
func fanIn(channels ...<-chan Result) <-chan Result {
var wg sync.WaitGroup
merged := make(chan Result)
for _, ch := range channels {
wg.Add(1)
go func(c <-chan Result) {
defer wg.Done()
for val := range c {
merged <- val
}
}(ch)
}
go func() {
wg.Wait()
close(merged)
}()
return merged
}
Real uses: Checking N servers in parallel, validating N config files, downloading N artifacts.
3 · Fill the gaps
Fan-in, from memory — the merged channel must end exactly when the last forwarder finishes.
func fanIn(channels ...<-chan Result) <-chan Result {
var wg sync.WaitGroup
merged := make(chan Result)
for _, ch := range channels {
wg.Add(1)
go func(c <-chan Result) {
defer <span class="gap-slot"><input class="gap-input" data-answer="wg.Done()" size="11" spellcheck="false" autocomplete="off" autocapitalize="off"></span>
for val := range c {
<span class="gap-slot"><input class="gap-input" data-answer="merged <- val" size="15" spellcheck="false" autocomplete="off" autocapitalize="off"></span>
}
}(ch)
}
go func() {
<span class="gap-slot"><input class="gap-input" data-answer="wg.Wait()" size="11" spellcheck="false" autocomplete="off" autocapitalize="off"></span>
<span class="gap-slot"><input class="gap-input" data-answer="close(merged)" size="15" spellcheck="false" autocomplete="off" autocapitalize="off"></span>
}()
return merged
}