// lesson: errgroup-from-scratch
errgroup From Scratch
Almost every fan-out in production code has the same requirements:
launch N tasks, wait for all of them, surface the first error, and
usually bound how many run at once. golang.org/x/sync/errgroup
packages exactly that, and its API is tiny:
var g errgroup.Group
g.SetLimit(4)
for _, u := range urls {
g.Go(func() error { return fetch(u) })
}
if err := g.Wait(); err != nil { /* first failure */ }
The value is in the precise semantics, so state them before building:
Go(f)runsfin its own goroutine. With a limit set,Goblocks until a slot frees up โ backpressure at the submission point, so a producer loop can never race ahead of the pool.Waitblocks until every launchedfhas returned, then yields the first non-nil error. Not the last, not a combined bag: the first, because later failures are usually just fallout from the first one.- The zero value is ready to use โ the Go idiom of making the empty
struct meaningful, like
bytes.Bufferandsync.Mutex.
Every piece is something you have already built in this course: a
WaitGroup for "all done", a first-writer-wins slot for the error
(exactly the Once semantics from earlier โ sync.Once is fair game
here), and a buffered-channel semaphore for the limit. The subtle part
is memory visibility: Wait reads the stored error after
wg.Wait(), and the failing goroutine wrote it before wg.Done(),
so the WaitGroup edge โ not luck โ is what makes the read safe.
The real errgroup also has errgroup.WithContext(ctx), which returns a
*Group paired with a derived context that is canceled the moment any
task returns an error โ the plain zero-value Group above has no
context of its own. That cancel-on-first-error shape is the same muscle
you built in ParallelMap, so here we focus on just the group.
โบ Build the Group
30 ptsImplement (the real errgroup is not available in the grader โ
stdlib only):
type Group struct { /* ... */ }
func (g *Group) SetLimit(n int) // max n tasks in flight; call before any Go
func (g *Group) Go(f func() error) // run f concurrently; block if at the limit
func (g *Group) Wait() error // wait for all; return the first error
Requirements:
- The zero value of
Groupis usable without a limit. - Tasks started with
Gorun concurrently, not synchronously. Waitreturns only after every task has finished, and returns the first error that occurred (nil if none).- After
SetLimit(n), at mostntasks are in flight at once;Goblocks while the group is full.
Log in to submit a solution and earn points.