Напишите fan-in merge, сводящий N входных каналов в один выходной
Реализуйте merge, принимающую любое число входных каналов и возвращающую один выходной канал со всеми значениями из них. Выходной канал должен закрыться ровно один раз, после полного опустошения всех входов, и ни одна горутина не должна утечь.
func merge(cs ...<-chan int) <-chan int {
// ваш код здесь
}
Допишите реализацию.
Запустите по горутине на каждый входной канал, каждая range-ит свой канал и шлёт все значения в общий out. sync.WaitGroup считает пересыльщиков; отдельная горутина зовёт wg.Wait(), затем close(out), поэтому выход закрывается ровно один раз после опустошения всех входов. out верните сразу. range по входам и закрытие out только после Wait исключают и панику send-on-closed, и утечку горутины.
- ✗Закрывать
outвнутри каждого пересыльщика, вызывая панику send-on-closed в других горутинах - ✗Использовать один
selectи считать, что один приём с канала означает его опустошение - ✗Никогда не закрывать
out, из-за чегоrangeвызывающего блокируется навсегда
- →Как добавить отмену, чтобы
mergeостанавливался раньше при отменеcontext.Context? - →Почему
close(out)должен быть в своей горутине, а не после циклаfor, запускающего пересыльщиков?
Задача
Слить N входных каналов в один выходной (fan-in). Выход закрывается один раз — после опустошения всех входов.
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(cs))
for _, c := range cs {
go func(c <-chan int) {
defer wg.Done()
for v := range c {
out <- v
}
}(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
Как это работает
- На каждый вход — своя горутина: она
range-ит канал (завершается, когда вход закрыт) и шлёт значения в общийout. WaitGroupсчитает пересыльщиков; отдельная горутина ждётwg.Wait()и только потом закрываетout— поэтому закрытие происходит один раз и после того, как все перестали слать (нет паникиsend on closed channel).outвозвращается сразу, до завершения горутин, чтобы вызывающий мог начатьrange merge(...)немедленно.
⚠️ Классическая ловушка: закрыть out внутри пересыльщика или забыть () у горутины закрытия — тогда range вызывающего зависнет.