当前位置: 首页 > 科技观察

Go语言slice原生支持并发吗?

时间:2023-03-17 00:00:54 科技观察

实践检验真理实践是检验真理的唯一标准,所以当我们遇到不确定的问题时,可以直接写一个demo来验证。由于切片的特性,我们可以在多种情况下进行验证:不指定索引、动态Scaleup并发向切片添加数据。funcconcurrentAppendSliceNotForceIndex(){sl:=make([]int,0)wg:=sync.WaitGroup{}forindex:=0;指数<100;index++{k:=indexwg.Add(1)gofunc(numint){sl=append(sl,num)wg.Done()}(k)}wg.Wait()fmt.Printf("finallen(sl)=%dcap(sl)=%d\n",len(sl),cap(sl))}打印数据,发现每次结果都不一致,先别急着下结论,我们正在写其他demo测试:指定索引,指定容量,向切片添加数据。funcconcurrentAppendSliceForceIndex(){sl:=make([]int,100)wg:=sync.WaitGroup{}forindex:=0;指数<100;index++{k:=indexwg.Add(1)gofunc(numint){sl[num]=numwg.Done()}(k)}wg.Wait()fmt.Printf("finallen(sl)=%dcap(sl)=%d\n",len(sl),cap(sl))}通过结果可以发现符合我们的预期,长度和容量都是100,slice也支持并发?slice支持并发吗?我们都知道slice是对数组的一种抽象,它的底层是一个数组。并发下向同一个索引位写入数据会被覆盖,slice也有自动扩容的功能。当切片需要扩展时,必须更换底层数组。切换底层数组时,多个goroutine同时运行,哪个goroutine先运行是不确定的,无论哪个goroutine先写内存,都必须有一次写入会覆盖之前的写入,所以并发写入内存是不安全的动态扩展时的数组;所以当别人问slice支持并发时,可以这样回答:指定索引使用slice时,slice支持并发读写索引区的数据,但是索引区的数据会并发时被覆盖;当不指定索引的时候分片,而分片动态扩容的时候,并发场景下扩容会被覆盖,所以分片不支持并发~。github上大名鼎鼎的iris框架也遇到了切片动态扩容导致webscoket连接数减少的bug。最后使用sync.map解决了这个问题。综上所述,针对以上问题,我们可以通过多种方式解决分片并发安全问题:添加互斥量、使用通道序列化操作、使用sync.map代替slice分片,都比较容易解决。不同的场景可以选择不同的方案进行方案优化,你学会了吗?