Ответ
errgroup
— это пакет из golang.org/x/sync/errgroup
, который предоставляет средства для синхронизации, распространения ошибок и отмены контекста для группы горутин, выполняющихся параллельно.
Он упрощает распространенный паттерн, когда нужно запустить несколько фоновых задач, дождаться их завершения и корректно обработать первую возникшую ошибку.
Основные компоненты и принцип работы:
errgroup.Group
: Основная структура, управляющая группой горутин.g.Go(func() error)
: Запускает переданную функцию в новой горутине. Если функция возвращает ошибку,errgroup
запоминает её.g.Wait() error
: Блокирует выполнение до тех пор, пока все горутины, запущенные черезg.Go()
, не завершатся. Возвращает первую не-nil
ошибку, полученную от любой из горутин.errgroup.WithContext()
: СоздаетGroup
и связанный с нейcontext.Context
. Этот контекст автоматически отменяется, как только одна из горутин возвращает ошибку. Это позволяет остальным горутинам грациозно завершить свою работу.
Пример: Выполнение нескольких HTTP-запросов параллельно.
package main
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 {
// Создаем запрос, который уважает отмену контекста
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err == nil {
resp.Body.Close()
}
fmt.Printf("Fetched %s with status: %sn", url, resp.Status)
return err
})
}
// Ждем завершения всех горутин и проверяем ошибку
if err := g.Wait(); err != nil {
fmt.Printf("nAn error occurred: %vn", err)
}
fmt.Println("All tasks finished.")
}