09.3.1 当Add()和Done()的数量不匹配时会发生什么?

sync.Add()sync.Done()调用的数量相等时,程序会正常运行。但是,本节将告诉你当调用数量不一致时会发生什么。

假如我们执行sync.Add()的次数大于执行sync.Done()的次数,这种情况下,通过在第一个fmt.Printf(“%#v \ n”,waitGroup)之前添加waitGroup.Add(1)语句,然后执行go run的输出如下:

$ go run syncGo.go
Going to create 20 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[12]uint8{0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, sema:0x0}
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[12]uint8{0x0, 0x0, 0x0, 0x0,
0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, sema:0x0}
19 10 11 12 13 17 18 8 5 4 6 14 1 0 7 3 2 15 9 16 fatal error: all
goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc4200120bc)
/usr/local/Cellar/go/1.9.3/libexec/src/runtime/sema.go:56 +0x39
sync.(*WaitGroup).Wait(0xc4200120b0)
/usr/local/Cellar/go/1.9.3/libexec/src/sync/waitgroup.go:131 +0x72
main.main()
/Users/mtsouk/Desktop/masterGo/ch/ch9/code/syncGo.go:28 +0x2d7
exit status 2

错误消息是很清楚的: fatal error: all goroutines are asleep - deadlock!.,这是因为你通过调用sync.Add(1)函数n+1次来告诉程序等待n+1goroutine,而ngoroutine只执行了nsync.Done()语句。因此,sync.Wait()调用将无限期地等待一个或多个对sync.Done()的调用,而不会有任何结果,这显然是死锁的情况。

如果使用的sync.Add()调用比sync.Done()调用少,那么可以通过在syncGo.go的for循环之后添加waitGroup.Done()语句来进行模拟。那么执行后输出类似如下的结果:

$ go run syncGo.go
Going to create 20 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[12]uint8{0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, sema:0x0}
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[12]uint8{0x0, 0x0, 0x0, 0x0,
0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, sema:0x0}
19 6 1 2 9 7 8 15 13 0 14 16 17 3 11 4 5 12 18 10 panic: sync: negative
WaitGroup counter
goroutine 22 [running]:
sync.(*WaitGroup).Add(0xc4200120b0, 0xffffffffffffffff)
/usr/local/Cellar/go/1.9.3/libexec/src/sync/waitgroup.go:75 +0x134
sync.(*WaitGroup).Done(0xc4200120b0)
/usr/local/Cellar/go/1.9.3/libexec/src/sync/waitgroup.go:100 +0x34
main.main.func1(0xc4200120b0, 0x11)
/Users/mtsouk/Desktop/masterGo/ch/ch9/code/syncGo.go:25 +0xd8
created by main.main
/Users/mtsouk/Desktop/masterGo/ch/ch9/code/syncGo.go:21 +0x206
exit status 2

这次问题的根源也非常清楚:panic: sync: negative WaitGroup counter。虽然这两种情况下的错误消息都非常具体,可以帮助你解决实际的问题,但是你应该非常小心地处理放入程序中的sync.Add()sync.Done()调用的数量。另外,请注意,第二个错误情况(panic: sync: negative WaitGroup counter)可能并不总是出现。

Last updated