Fan-in: merge
Каждый воркер отдавал результаты в свой канал, и следующая стадия не знала, сколько их было. Миша написал merge: результаты стеклись в один канал, а закрыла его отдельная горутина, когда все воркеры закончили.
merge собирает несколько каналов в один: на каждый вход — своя горутина-передатчик, а выходной канал закрывает третья горутина, когда все передатчики закончили.
Merge
func merge(chs ...<-chan *FileMeta) <-chan *FileMeta {
out := make(chan *FileMeta)
var wg sync.WaitGroup
for _, ch := range chs {
wg.Add(1)
go func(ch <-chan *FileMeta) {
defer wg.Done()
for v := range ch {
out <- v
}
}(ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
Каждый входной канал получает свою горутину: она читает и пересылает в общий out. Третья горутина ждёт все передатчики через WaitGroup и закрывает out — иначе читатель merge никогда бы не узнал, что данные кончились.
Три роли
Обрати внимание на разделение ролей: передатчики читают входы, WaitGroup считает их, закрывающая горутина — единственный владелец close(out). Это тот самый вопрос «кто закрывает» из, решённый правильно: закрывает не один из отправителей, а отдельная горутина, знающая, когда все закончили.
Fan-out + fan-in = пайплайн
Fan-out и fan-in обычно живут в одном пайплайне: стадия конвейера загрузки читает вход, раздаёт работу воркерам (fan-out), собирает результаты в один канал (fan-in) и отдаёт следующей стадии. Следующая стадия — снова fan-out — и так цепочкой.
Вы узнаете:
- почему нельзя без WaitGroup
- как merge обобщается на дженерики
Merge: горутина на вход, общий выход, закрытие через WaitGroup. Пары fan-out/fan-in складываются в пайплайны стадий.
10 000 ₽ · 60 минут · онлайн