Запустите читателя для каждого входного канала и пересылайте значения в общий выход. WaitGroup отслеживает завершение читателей; отдельная горутина закрывает выход после Wait. В main прочитайте его через range и затем напечатайте ok. Входы закрывают их производители; порядок значений между разными каналами не гарантирован.
Как объединить несколько каналов Go в один и вывести ok после чтения всех значений?
Решение fan-in через отдельные горутины и WaitGroup. Выход закрывается только после всех отправок, а ok выводится после завершения range.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие. Реализовать merge, принимающую произвольное количество chan int и возвращающую общий канал. Производители закрывают входы после записи. После чтения всех значений программа печатает ok. Предполагаются ненулевые входные каналы и чтение выхода до конца.
package main
import (
"fmt"
"sync"
)
func merge(inputs ...chan int) <-chan int {
result := make(chan int)
var readers sync.WaitGroup
readers.Add(len(inputs))
for _, input := range inputs {
go func(in <-chan int) {
defer readers.Done()
for value := range in {
result <- value
}
}(input)
}
go func() {
readers.Wait()
close(result)
}()
return result
}
func main() {
first, second := make(chan int), make(chan int)
go func() {
defer close(first)
first <- 1
first <- 2
}()
go func() {
defer close(second)
second <- 3
second <- 4
}()
for value := range merge(first, second) {
fmt.Println(value)
}
fmt.Println("ok")
}
Счётчик увеличивается до запуска горутин. Каждый читатель заканчивает только после закрытия своего входа и пересылки значений. Поэтому после Wait новых отправок в выход не будет и его можно безопасно закрыть. Входы внутри merge не закрываются. При отсутствии входов выход тоже закроется.
Общий порядок чисел не фиксирован; ok гарантированно следует после них. Для N значений и k каналов работа занимает O(N + k), дополнительные горутины — O(k).
Если потребитель прекращает чтение раньше времени, отправители могут зависнуть. Для такого контракта нужна отмена и её поддержка производителями; базовый вариант её не обещает. Принцип fan-in и отмена описаны в Go Blog.