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): the 1 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(): the defer keyword and the appearance of Done 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)
TL;DR
Add a
func (*WaitGroup) Go(task func())method to launch a task in a goroutine tracked with async.WaitGroup.Combined with the loopvar change (#60078), writing parallel code would be much less error prone.
Rationale
A very common use case for
sync.WaitGroupis to track the termination of tasks launched in goroutines.Here is the classic example:
I propose to add a
func (*WaitGroup) Go(func())method that would wrap:wg.Add(1): the1looks like a magic valuegokeyword and the()after the func body are magic for Go beginnersdefer wg.Done(): thedeferkeyword and the appearance ofDonebefore the call to the worker are magicA simple implementation:
The example above would be much reduced and many footguns avoided (the last remaining footgun is being addressed by #60078):
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
workto use the builder pattern: https://go.dev/play/p/g1Um_GhQOyc)