sync: add WaitGroup.Go

Fixes #63796

Change-Id: I2a941275dd64ef858cbf02d31a759fdc5c082ceb
Reviewed-on: https://go-review.googlesource.com/c/go/+/662635
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Auto-Submit: Carlos Amedee <carlos@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
This commit is contained in:
qiulaidongfeng 2025-04-03 23:10:16 +08:00 committed by Gopher Robot
parent d164776615
commit 822031dffc
4 changed files with 60 additions and 2 deletions

1
api/next/63796.txt Normal file
View File

@ -0,0 +1 @@
pkg sync, method (*WaitGroup) Go(func()) #63769

View File

@ -0,0 +1,2 @@
[WaitGroup] has added a new method [WaitGroup.Go],
that makes the common pattern of creating and counting goroutines more convenient.

View File

@ -10,12 +10,35 @@ import (
"unsafe" "unsafe"
) )
// A WaitGroup waits for a collection of goroutines to finish. // A WaitGroup is a counting semaphore typically used to wait
// The main goroutine calls [WaitGroup.Add] to set the number of // for a group of goroutines to finish.
//
// The main goroutine calls [WaitGroup.Add] to set (or increase) the number of
// goroutines to wait for. Then each of the goroutines // goroutines to wait for. Then each of the goroutines
// runs and calls [WaitGroup.Done] when finished. At the same time, // runs and calls [WaitGroup.Done] when finished. At the same time,
// [WaitGroup.Wait] can be used to block until all goroutines have finished. // [WaitGroup.Wait] can be used to block until all goroutines have finished.
// //
// This is a typical pattern of WaitGroup usage to
// synchronize 3 goroutines, each calling the function f:
//
// var wg sync.WaitGroup
// for range 3 {
// wg.Add(1)
// go func() {
// defer wg.Done()
// f()
// }()
// }
// wg.Wait()
//
// For convenience, the [WaitGroup.Go] method simplifies this pattern to:
//
// var wg sync.WaitGroup
// for range 3 {
// wg.Go(f)
// }
// wg.Wait()
//
// A WaitGroup must not be copied after first use. // A WaitGroup must not be copied after first use.
// //
// In the terminology of [the Go memory model], a call to [WaitGroup.Done] // In the terminology of [the Go memory model], a call to [WaitGroup.Done]
@ -127,3 +150,23 @@ func (wg *WaitGroup) Wait() {
} }
} }
} }
// Go calls f in a new goroutine and adds that task to the WaitGroup.
// When f returns, the task is removed from the WaitGroup.
//
// If the WaitGroup is empty, Go must happen before a [WaitGroup.Wait].
// Typically, this simply means Go is called to start tasks before Wait is called.
// If the WaitGroup is not empty, Go may happen at any time.
// This means a goroutine started by Go may itself call Go.
// If a WaitGroup is reused to wait for several independent sets of tasks,
// new Go calls must happen after all previous Wait calls have returned.
//
// In the terminology of [the Go memory model](https://go.dev/ref/mem),
// the return from f "synchronizes before" the return of any Wait call that it unblocks.
func (wg *WaitGroup) Go(f func()) {
wg.Add(1)
go func() {
defer wg.Done()
f()
}()
}

View File

@ -98,6 +98,18 @@ func TestWaitGroupAlign(t *testing.T) {
x.wg.Wait() x.wg.Wait()
} }
func TestWaitGroupGo(t *testing.T) {
wg := &WaitGroup{}
var i int
wg.Go(func() {
i++
})
wg.Wait()
if i != 1 {
t.Fatalf("got %d, want 1", i)
}
}
func BenchmarkWaitGroupUncontended(b *testing.B) { func BenchmarkWaitGroupUncontended(b *testing.B) {
type PaddedWaitGroup struct { type PaddedWaitGroup struct {
WaitGroup WaitGroup