Хватит мучить sync.
Каждый джун проходит этот путь:
1. Запускаем 10 горутин через go func().
2. Добавляем wg.Add(1).
3. Понимаем, что одна горутина может вернуть ошибку.
4. Создаем канал для ошибок, мьютекс или (о ужас) глобальную переменную.
5. Код превращается в нечитаемую простыню.
Коллеги, для групповых задач с возвратом ошибок есть стандартный (почти) инструмент - errgroup.
Пакет golang.org/x/sync/errgroup делает три вещи, которые вы обычно пишете руками:
1. Ждет завершения всех горутин.
2. Возвращает первую случившуюся ошибку.
3. (Опционально) Отменяет контекст для остальных, если кто-то один упал.
Как это выглядит:
import "golang.org/x/sync/errgroup"
func fetchAll(urls []string) error {
// Создаем группу и контекст
g, _ := errgroup.WithContext(context.Background())
for _, url := range urls {
url := url // Go 1.22 fix not needed, but habit 🙂
// g.Go принимает функцию вида func() error
g.Go(func() error {
resp, err := http.Get(url)
if err == nil {
resp.Body.Close()
}
return err // Если вернем err != nil, контекст отменится
})
}
// Ждем всех. Если была ошибка — получим её.
return g.Wait()
}
🔥 Senior Tip:
В последних версиях добавили метод g.SetLimit(n).
Это киллер-фича. Она превращает errgroup в Worker Pool с ограничением конкурентности. Больше не нужно создавать семафоры на каналах, чтобы не за-DDOS-ить внешний API. Просто добавьте g.SetLimit(10) перед циклом.
Чисто, лаконично, и никаких дедлоков на wg.Done().
#golang #concurrency #patterns #bestpractices
👉 @golang_lib