Как вернуть значение из функции при чтении из канала несколькими горутинами в Go
Что проверяют
Умеете ли вы собрать результаты нескольких горутин без гонок, дедлоков и утечек. Главное здесь: кто закрывает канал и когда функции можно вернуть значение. Следом обычно спрашивают, что делать, если нужен только первый результат или одна из горутин вернула ошибку.
Как отвечать
Типовая схема fan-out/fan-in: N воркеров читают канал jobs через for j := range jobs и пишут в results, функция собирает results и возвращает итог.
var wg sync.WaitGroup
for range n {
wg.Add(1)
go func() {
defer wg.Done()
for j := range jobs {
results <- process(j)
}
}()
}
go func() { wg.Wait(); close(results) }()
sum := 0
for r := range results {
sum += r
}
return sumЧто проговорить:
- закрывает отправитель:
jobsзакрывает тот, кто в него пишет,resultsзакрывает отдельная горутина послеwg.Wait(), воркер не закрывает ничего; Addдоgo: иначеWaitможет проскочить раньше. С Go 1.25 для этого естьwg.Go(func() {...});- ошибки:
errgroupизgolang.org/x/syncвозвращает первую ошибку изg.Wait(),errgroup.WithContextотменяет контекст остальным,g.SetLimit(n)ограничивает параллельность; - первый результат: буфер канала на N значений или
selectсctx.Done(), чтобы проигравшие горутины не зависли на отправке; - сбор в срез: запись в свой
res[i]из разных горутин безопасна,appendв общий срез без мьютекса это гонка.
Всё проверяется запуском с -race.
Где спотыкаются
Закрывают канал в воркере. Второй воркер ловит panic на отправке в закрытый канал или на повторном close.
Возвращаются после первого значения из небуферизованного канала. Остальные горутины навсегда блокируются на отправке, и утечку видно только по росту runtime.NumGoroutine().
Пишут результат в общую переменную и надеются на time.Sleep. Гонка проявляется не на каждом запуске, поэтому локально код кажется рабочим.