自学内容网 自学内容网

golang sync.Pool 指针数据覆盖问题

场景

1. sync.Pool设置

var stringPool = sync.Pool{
New: func() any {
return new([]string)
},
}

func NewString() *[]string {
v := stringPool.Get().(*[]string)
return v
}

func PutString(s *[]string) {
if s == nil {
return
}

if cap(*s) > 2048 {
s = nil
} else {
*s = (*s)[:0]
stringPool.Put(s)
}
}

2.使用sync.Pool

func Test_Pool(t *testing.T) {
dataSlice1 := demoData()
dataSlice2 := demoData()
dataSlice2[1] = "test4"

fmt.Printf("dataSlice1:%v %p,dataSlice2:%v %p\n", dataSlice1, dataSlice1, dataSlice2, dataSlice2)
}

func demoData() []string {
strsPtr := NewString()
strs := *strsPtr
defer func() {
*strsPtr = strs
PutString(strsPtr)
}()

strs = append(strs, "test1", "test2")
return strs
}

打印结果:dataSlice1:[test1 test4] 0xc0000a6400,dataSlice2:[test1 test4] 0xc0000a6400

可以看到使用了同一个指针地址,导致两次获取的数据互相影响

3.解决方法1

func Test_Pool(t *testing.T) {
dataSlice1 := demoData()
dataSlice2 := demoData()
dataSlice2[1] = "test4"

fmt.Printf("dataSlice1:%v %p,dataSlice2:%v %p\n", dataSlice1, dataSlice1, dataSlice2, dataSlice2)
}

func demoData() []string {
strsPtr := NewString()
strs := *strsPtr
defer func() {
*strsPtr = strs
PutString(strsPtr)
}()

strs = append(strs, "test1", "test2")

// 深复制
var items = make([]string, len(strs))
copy(items, strs)

return items
}

使用深复制,在put回sync.Pool中之前把数据复制返回,但这样资源池失去了意义,获取到资源后有进行了一次内存的申请

4.解决方法2

我们看下golang语言源码怎么解决的

参考 go/src/fmt/print.go 302行 Fprintln方法

func Fprintln(w io.Writer, a ...any) (n int, err error) {
p := newPrinter()
p.doPrintln(a)
n, err = w.Write(p.buf)
p.free()
return
}

可以看到306行有p.free()代码,newPrinter()和free()之间进行数据处理,数据处理完成之后再把资源返回给sync.Pool

总结:不是任何场景都适合用sync.Pool,需要关注并发情况下资源池中数据同步修改影响的问题。


原文地址:https://blog.csdn.net/m0_38031406/article/details/136560946

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!