Ответ
errgroup
— это пакет (golang.org/x/sync/errgroup
), который предоставляет средства для синхронизации группы горутин и обработки ошибок в них. Он является расширением sync.WaitGroup
со встроенной обработкой ошибок и отменой контекста.
Основные возможности
- Синхронизация: Метод
g.Wait()
блокирует выполнение до тех пор, пока все горутины, запущенные черезg.Go()
, не завершатся. - Обработка ошибок:
g.Wait()
возвращает первую не-nil
ошибку, полученную от любой из горутин в группе. Остальные ошибки игнорируются. - Отмена через контекст: При использовании
errgroup.WithContext()
первая же ошибка приводит к отмене контекста. Это позволяет остальным горутинам в группе грациозно завершить свою работу, не выполняя лишних действий. - Ограничение параллелизма: Метод
g.SetLimit(n)
позволяет ограничить количество одновременно выполняющихся горутин, что превращаетerrgroup
в простой и эффективный пул воркеров.
Пример использования
Представим, что нужно параллельно выполнить несколько HTTP-запросов и остановиться, если хотя бы один из них завершился с ошибкой.
import (
"context"
"fmt"
"net/http"
"golang.org/x/sync/errgroup"
)
func main() {
g, ctx := errgroup.WithContext(context.Background())
urls := []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://invalid-url/", // Этот запрос вызовет ошибку
}
for _, url := range urls {
url := url // https://golang.org/doc/faq#closures_and_goroutines
g.Go(func() error {
// При ошибке в другой горутине ctx будет отменен
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("Ошибка при запросе %s: %vn", url, err)
return err // Возвращаем ошибку
}
resp.Body.Close()
fmt.Printf("Успешно получен ответ от %sn", url)
return nil
})
}
// Ожидаем завершения всех горутин и проверяем ошибку
if err := g.Wait(); err != nil {
fmt.Printf("nОдна из операций завершилась с ошибкой: %vn", err)
}
}
Когда использовать?
errgroup
идеально подходит для сценариев, где нужно выполнить несколько независимых задач параллельно и результат всей операции считается неудачным, если хотя бы одна из подзадач провалилась. Например:
- Параллельные запросы к разным микросервисам или базам данных.
- Параллельная обработка набора файлов или данных.
- Реализация Scatter-Gather паттерна.