Как объединить несколько каналов Go в один и вывести ok после чтения всех значений?

Решение fan-in через отдельные горутины и WaitGroup. Выход закрывается только после всех отправок, а ok выводится после завершения range.

Короткий ответ

Что ответить на собеседовании

Запустите читателя для каждого входного канала и пересылайте значения в общий выход. WaitGroup отслеживает завершение читателей; отдельная горутина закрывает выход после Wait. В main прочитайте его через range и затем напечатайте ok. Входы закрывают их производители; порядок значений между разными каналами не гарантирован.

Подробный разбор

Ответ с пояснениями

Условие. Реализовать 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.

Практика в реальном времени

Подготовьтесь к следующему собеседованию

Interview Boost учитывает вакансию, резюме и технологии и помогает сформулировать ответ прямо во время интервью.

Начать подготовку