Description
TL;DR
Add a func (*WaitGroup) Go(task func())
method to launch a task in a goroutine tracked with a sync.WaitGroup
.
Combined with the loopvar change (#60078), writing parallel code would be much less error prone.
Rationale
A very common use case for sync.WaitGroup
is to track the termination of tasks launched in goroutines.
Here is the classic example:
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
i := i
wg.Add(1)
go func() {
defer wg.Done()
work(i)
}()
}
wg.Wait()
I propose to add a func (*WaitGroup) Go(func())
method that would wrap:
wg.Add(1)
: the1
looks like a magic value- launching the goroutine: the
go
keyword and the()
after the func body are magic for Go beginners defer wg.Done()
: thedefer
keyword and the appearance ofDone
before the call to the worker are magic
A simple implementation:
func (wg *WaitGroup) Go(task func()) {
wg.Add(1)
go func() {
defer wg.Done()
task()
}()
}
The example above would be much reduced and many footguns avoided (the last remaining footgun is being addressed by #60078):
var wg WaitGroup
for i := 1; i <= 5; i++ {
i := i // avoid loopvar footgun for go < 1.22
wg.Go(func() {
work(i)
})
}
wg.Wait()
The full modified example, including an extended implementation of sync.WaitGroup
, is available on the Go playground.
(to handle the case of a task with arguments, I would recommend rewriting the work
to use the builder pattern: https://go.dev/play/p/g1Um_GhQOyc)
Metadata
Metadata
Assignees
Type
Projects
Status