在Go中,没有值可以安全地进行并行读/写操作,切片(切片头)也不例外。
是的,您的代码存在数据竞争。运行带有
-race选项的验证。
例:
type myClass struct { AttributeName string}sourceSlice := make([]myClass, 100)destSlice := make([]myClass, 0)var wg sync.WaitGroupfor _, myObject := range sourceSlice { wg.Add(1) go func(closureMyObject myClass) { defer wg.Done() var tmpObj myClass tmpObj.AttributeName = closureMyObject.AttributeName destSlice = append(destSlice, tmpObj) }(myObject)}wg.Wait()运行它
go run -race play.go
输出为:
==================WARNING: DATA RACERead at 0x00c420074000 by goroutine 6: main.main.func1() /home/icza/gows/src/play/play.go:20 +0x69Previous write at 0x00c420074000 by goroutine 5: main.main.func1() /home/icza/gows/src/play/play.go:20 +0x106Goroutine 6 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cbGoroutine 5 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cb====================================WARNING: DATA RACERead at 0x00c42007e000 by goroutine 6: runtime.growslice() /usr/local/go/src/runtime/slice.go:82 +0x0 main.main.func1() /home/icza/gows/src/play/play.go:20 +0x1a7Previous write at 0x00c42007e000 by goroutine 5: main.main.func1() /home/icza/gows/src/play/play.go:20 +0xc4Goroutine 6 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cbGoroutine 5 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cb====================================WARNING: DATA RACEWrite at 0x00c420098120 by goroutine 80: main.main.func1() /home/icza/gows/src/play/play.go:20 +0xc4Previous write at 0x00c420098120 by goroutine 70: main.main.func1() /home/icza/gows/src/play/play.go:20 +0xc4Goroutine 80 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cbGoroutine 70 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cb==================Found 3 data race(s)exit status 66
解决方法很简单,使用一个
sync.Mutex保护写
destSlice值:
var ( mu = &sync.Mutex{} destSlice = make([]myClass, 0))var wg sync.WaitGroupfor _, myObject := range sourceSlice { wg.Add(1) go func(closureMyObject myClass) { defer wg.Done() var tmpObj myClass tmpObj.AttributeName = closureMyObject.AttributeName mu.Lock() destSlice = append(destSlice, tmpObj) mu.Unlock() }(myObject)}wg.Wait()您还可以通过其他方式解决它,例如,可以使用一个通道,在该通道上发送要附加的值,并从该通道接收指定的goroutine并执行附加操作。



