Fan-Out и Fan-In
Распределяйте и собирайте работу
«Fan-Out и Fan-In» — бесплатный урок Go Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.
Распределение и объединение
Распределение означает запуск нескольких горутин, читающих из одного канала и распределяющих работу. Объединение означает сведение выходных данных нескольких горутин обратно в один канал.
- Распределение равномерно распределяет нагрузку между worker
- Объединение сводит результаты для потребителя
Stage-генератор
Конвейер начинается с генератора: функции, которая возвращает канал и передаёт в него значения из собственной горутины, а затем закрывает канал.
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}Распределение работы
Чтобы распределить работу, запустите несколько горутин, которые читают из одного входного канала. Каждая выполняет одну и ту же функцию обработки и создаёт собственный выходной канал.
in := gen(1, 2, 3, 4, 5)
c1 := square(in)
c2 := square(in)Stage обработки
Stage square читает данные из in, возводит каждое значение в квадрат и отправляет его в новый выходной канал. Несколько экземпляров этого stage используют один вход.
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}Объединение с помощью merge
Объединение сводит несколько каналов в один. Объект sync.WaitGroup отслеживает горутины для отдельных каналов, а горутина-закрыватель ждёт завершения всех из них, прежде чем закрыть объединённый выходной канал.
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, c := range cs {
wg.Add(1)
go func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}(c)
}
go func() { wg.Wait(); close(out) }()
return out
}Зачем нужен WaitGroup
Каждая горутина объединения копирует один входной канал в out. Мы не должны закрывать out, пока все входные каналы не будут полностью прочитаны. WaitGroup подсчитывает активные горутины-копировщики; горутина-закрыватель блокируется на wg.Wait(), пока они не завершатся.
Сохранение переменной цикла
В старых версиях Go замыкание должно принимать c в качестве параметра, чтобы каждая горутина связывалась со своим каналом, а не с общей переменной цикла. Явная передача c предотвращает классическую ошибку с переменной цикла.
Полное распределение и объединение
Эта программа генерирует числа, распределяет их между двумя stage square, затем объединяет результаты и вычисляет их сумму.
package main
import (
"fmt"
"sync"
)
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() { for _, n := range nums { out <- n }; close(out) }()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() { for n := range in { out <- n * n }; close(out) }()
return out
}
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, c := range cs {
wg.Add(1)
go func(c <-chan int) { for n := range c { out <- n }; wg.Done() }(c)
}
go func() { wg.Wait(); close(out) }()
return out
}
func main() {
in := gen(1, 2, 3, 4, 5)
c1 := square(in)
c2 := square(in)
total := 0
for n := range merge(c1, c2) {
total += n
}
fmt.Println("sum of squares:", total)
}Порядок не гарантирован
Поскольку два stage square одновременно обрабатывают один и тот же вход, порядок объединённых результатов недетерминирован. Если порядок важен, добавьте к каждому элементу индекс или используйте один stage.
Когда распределять работу
Распределяйте работу, когда stage является узким местом, а задачу можно выполнять параллельно. Если вышестоящий генератор работает медленно, добавление worker ниже по конвейеру не поможет: они просто будут простаивать.
Обратное давление
Небуферизованные каналы создают естественное обратное давление: быстрый stage блокируется, когда медленный потребитель не успевает за ним. Это предотвращает неограниченный рост объёма памяти во всём конвейере.
Быстрая проверка
Проверьте, насколько хорошо Вы понимаете объединение каналов.
Итоги
Вы изучили распределение и объединение:
- Распределение: несколько горутин читают из одного канала
- Объединение: сведение множества каналов в один с помощью WaitGroup
- Порядок элементов не сохраняется между параллельными stage
- Небуферизованные каналы создают обратное давление
Часто задаваемые вопросы
Урок «Fan-Out и Fan-In» бесплатный?
Да — полный текст урока «Fan-Out и Fan-In» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.
Чему я научусь в уроке «Fan-Out и Fan-In»?
Распределяйте и собирайте работу Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Go Academy?
Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Fan-Out и Fan-In»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Go Academy?
Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Паттерн пула работников
- Fan-Out и Fan-In
- Этапы конвейера
- Корректное завершение