At the moment, Go 1.26 is in RC, but before looking at the new features, it may be useful to review something that was introduced in Go 1.25.
In this post, we look at WaitGroup.Go(). The purpose of this function is to create a goroutine and automatically increment the counter used by the WaitGroup to track when all goroutines have completed.
Let’s see an example using what we had in version 1.24:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(1000 * time.Millisecond)
fmt.Printf("Goroutine %d \n", i)
}()
}
wg.Wait()
fmt.Println("All goroutines completed")
}For every goroutine, we have to call Add(1), and inside the function body we need to call Done().
Instead, using WaitGroup.Go():
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Go(func() {
time.Sleep(1000 * time.Millisecond)
fmt.Printf("Goroutine %d \n", i)
})
}
wg.Wait()
fmt.Println("All goroutines completed")
}
Thus, the Add and Done operations are implicit. Take a look at the implementation:
func (wg *WaitGroup) Go(f func()) {
wg.Add(1)
go func() {
defer func() {
if x := recover(); x != nil {
// f panicked, which will be fatal because
// this is a new goroutine.
//
// Calling Done will unblock Wait in the main goroutine,
// allowing it to race with the fatal panic and
// possibly even exit the process (os.Exit(0))
// before the panic completes.
//
// This is almost certainly undesirable,
// so instead avoid calling Done and simply panic.
panic(x)
}
// f completed normally, or abruptly using goexit.
// Either way, decrement the semaphore.
wg.Done()
}()
f()
}()
}https://cs.opensource.google/go/go/+/master:src/sync/waitgroup.go