← Назад к вопросам
Объединение каналов (Fan-In)
1.7 Middle🔥 261 комментариев
#Конкурентность и горутины#Основы Go
Условие
Напишите функцию, которая принимает несколько каналов для чтения целых чисел и объединяет их в один канал для чтения.
Сигнатура
func merge(channels ...<-chan int) <-chan int
Требования
- Функция должна возвращать канал, из которого можно читать значения из всех входных каналов
- Функция должна корректно закрывать выходной канал, когда все входные каналы закрыты
- Использовать горутины и sync.WaitGroup для ожидания завершения
Пример использования
ch1 := make(chan int)
ch2 := make(chan int)
merged := merge(ch1, ch2)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Это классический паттерн Fan-In в Go — объединение нескольких каналов в один. Задача требует корректного управления жизненным циклом каналов и синхронизации горутин.
Подход
- Создаём выходной канал
- Для каждого входного канала запускаем горутину, которая читает из него
- Используем sync.WaitGroup для отслеживания завершения всех горутин
- Закрываем выходной канал, когда все входные каналы исчерпаны
Реализация
package main
import "sync"
func merge(channels ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
// Функция для чтения из одного канала и отправки в выходной
sendToOut := func(ch <-chan int) {
defer wg.Done()
for val := range ch {
out <- val
}
}
// Запускаем горутину для каждого входного канала
wg.Add(len(channels))
for _, ch := range channels {
go sendToOut(ch)
}
// Отдельная горутина для закрытия выходного канала
go func() {
wg.Wait() // Ждём, пока все горутины завершатся
close(out) // Закрываем выходной канал
}()
return out
}
Почему именно этот подход
- WaitGroup: правильно отслеживает, когда все горутины завершены
- defer wg.Done(): гарантирует вызов даже при паник
- Отдельная горутина для close: избегаем deadlock и ошибки "send on closed channel"
- range ch: автоматически обнаруживает закрытие канала
Пример использования
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
merged := merge(ch1, ch2)
// Запускаем горутины для отправки данных
go func() {
ch1 <- 1
ch1 <- 2
close(ch1)
}()
go func() {
ch2 <- 3
ch2 <- 4
close(ch2)
}()
// Читаем из объединённого канала
for val := range merged {
println(val) // выведет: 1, 2, 3, 4 (или другой порядок)
}
}
Возможные ошибки и как их избежать
❌ Ошибка: закрывать выходной канал в основной горутине
- Может привести к deadlock, если горутины ещё пишут
✅ Правильно: использовать отдельную горутину + WaitGroup
- Гарантирует, что все писатели завершены до close
❌ Ошибка: забыть закрыть выходной канал
- Читатель будет ждать вечно
Временная сложность
- O(n) — где n общее количество значений во всех каналах
- Каждое значение обрабатывается ровно один раз
Этот паттерн — стандартный способ работы с множеством каналов в Go и часто используется в production коде для обработки параллельных потоков данных.