该代码在多个goroutine中并发运行,以下代码是从生产环境代码中提取的关键相关部分:
func check() {
.......check condition......
//skipEnsure and skipNative will not both false here
var wg sync.WaitGroup
if !skipEnsure {
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
dosomething1()
}(&wg)
}
if !skipNative {
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
dosomething2()
}(&wg)
}
wg.Wait() //panic here
}
func worker() {
......
go check()
}
func main() {
for i:=0;i<32;i++ {
go worker()
}
ch := make(chan any, 0)
<-ch
}
当 WaitGroup 是全局变量时,我可以重现该问题,但不能在提供的代码中重现该问题。在生产环境运行一段时间后会出现panic。
如果我理解正确的话,代码看起来像这样:
1 package main
2
3 import (
4 "sync"
5 "time"
6 )
7
8 var wg sync.WaitGroup
9
10 func work() {
11 wg.Add(1)
12 go func(wg *sync.WaitGroup) {
13 defer wg.Done()
14 time.Sleep(1 * time.Millisecond)
15 println("one done")
16 }(&wg)
17 wg.Add(1)
18 go func(wg *sync.WaitGroup) {
19 defer wg.Done()
20 time.Sleep(2 * time.Millisecond)
21 println("two done")
22 }(&wg)
23 wg.Wait() //panic here
24 }
25
26 func main() {
27 for i := 0; i < 10; i++ {
28 go work()
29 }
30 wg.Wait()
31 println("done")
32 }
sync.WaitGroup 不是为这种用途而设计的。何时调用 wg.Add 和 wg.Wait 并不明显。如果您尝试使用数据竞争检测器运行它,您可以看到类似这样的内容:
==================
WARNING: DATA RACE
Write at 0x0000011707e8 by goroutine 6:
runtime.racewrite()
<autogenerated>:1 +0x24
main.work()
/1.go:23 +0x1b2
Previous read at 0x0000011707e8 by goroutine 7:
runtime.raceread()
<autogenerated>:1 +0x24
main.work()
/1.go:11 +0x35
Goroutine 6 (running) created at:
main.main()
/1.go:28 +0x32
Goroutine 7 (running) created at:
main.main()
/1.go:28 +0x32
==================
Found 1 data race(s)
您可以为每个 goroutine 使用自己的 WaitGroup (如您的示例所示)。我无法想象有什么理由反对它。例如,这段代码可以正常工作,没有任何数据竞争:
package main
import (
"sync"
"time"
)
func work() {
var wg sync.WaitGroup
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(1 * time.Millisecond)
println("one done")
}(&wg)
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(2 * time.Millisecond)
println("two done")
}(&wg)
wg.Wait() //panic here
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
work()
wg.Done()
}()
}
wg.Wait()
println("done")
}