并发切片:附加不是线程安全的
问题:
考虑以下代码在 for 循环中使用多个 goroutine 附加到切片:
destSlice := make([]myClass, 0) var wg sync.WaitGroup for _, 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()
有时,此代码会在 destSlice 中生成丢失或空白的数据,或者不包含 sourceSlice 中的所有元素。为什么会发生这种情况?
答案:
在 Go 中,当进行并发读/写操作(包括切片)时,所有值都容易受到数据争用的影响。这是由于切片标头(其中包含有关切片的容量和长度的信息)的实现方式所致。
使用 -race 标志运行代码将确认数据争用的存在:
go run -race play.go
解决方案:
为了避免数据竞争并确保附加到切片时的并发安全,应使用同步原语(例如sync.Mutex)来保护写入 destSlice 值:
var ( mu = &sync.Mutex{} destSlice = make([]myClass, 0) ) var wg sync.WaitGroup for _, 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 中的切片不是线程安全的?的详细内容。更多信息请关注PHP中文网其他相关文章!