为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 golang 中重用 slice with sync.Pool?

如何在 golang 中重用 slice with sync.Pool?

Go
富国沪深 2023-01-03 11:17:42
考虑这段代码:type TestStruct struct {    Name string}func TestSliceWithPool(t *testing.T) {    var slicePool = sync.Pool{        New: func() interface{} {            t.Log("i am created")            s := make([]interface{}, 0)            return s        },    }    s, _ := slicePool.Get().([]interface{})    t.Logf("Lenth: %d, Cap: %d, Pointer: %p", len(s), cap(s), s)    for i := 0; i < 9000; i++ {        st := &TestStruct{Name: "test"}        s = append(s, st)    }    for _, v := range s {        if value, ok := v.(TestStruct); ok {            if value.Name != "test" {                t.Error("u are changed!")            }        }    }    s = s[:0]    slicePool.Put(s)    s2, _ := slicePool.Get().([]interface{})    t.Logf("Lenth: %d, Cap: %d, Pointer: %p", len(s), cap(s), s)    for i := 0; i < 8000; i++ {        st := &TestStruct{Name: "test2"}        s2 = append(s2, st)    }    for _, v := range s2 {        if value, ok := v.(TestStruct); ok {            if value.Name != "test2" {                t.Error("u are changed!")            }        }    }    slicePool.Put(s2)}测试结果为:slice_test.go:63: i am createdslice_test.go:70: Lenth: 0, Cap: 0, Pointer: 0x1019598slice_test.go:86: Lenth: 0, Cap: 9728, Pointer: 0xc000500000为什么只生成一次,地址却不同?为什么上限是 9728?像这样使用同一个切片有什么问题吗?
查看完整描述

1 回答

?
斯蒂芬大帝

TA贡献1827条经验 获得超8个赞

为什么只生成一次,地址却不同?

因为在第一个 for 循环中,您将其追加到它的容量之外,它的 inNew设置为零,并将 的结果重新分配给它append。详见:为什么append()会修改提供的切片?(见例子)

像这样使用同一个切片有什么问题吗?

可能有。当您使用 reslicess = s[:0],您重置的是长度而不是容量。后备数组与之前的附加和重新分配操作中的相同。

因此,如果您再次追加到s2,容量将足以不会导致重新分配,并且您最终将覆盖后备数组的第一个元素:

一个示范性的例子:

func TestSliceWithPool(t *testing.T) {

    var slicePool = sync.Pool{

        New: func() interface{} {

            t.Log("Created")

            s := make([]interface{}, 0)

            return s

        },

    }


    s, _ := slicePool.Get().([]interface{})

    for i := 0; i < 10; i++ {

        s = append(s, i)

    }

    fmt.Println(s) 

    // ^ output: [0 1 2 3 4 5 6 7 8 9]


    s = s[:0]

    slicePool.Put(s)


    s2, _ := slicePool.Get().([]interface{})

    fmt.Println(s)

    // ^ output: []


    for i := 0; i < 5; i++ {

        s2 = append(s2, i*10)

    }

    fmt.Println(s2) 

    // ^ output: [0 10 20 30 40]


    fmt.Println(s2[:10])

    // ^ output: [0 10 20 30 40 5 6 7 8 9]

}

这可能没问题,因为你现在有一个扩展容量的切片,不需要在追加时重新分配,但如果你的应用程序保持其他切片头指向相同的支持数组(如s和s2),从而防止缓冲区的垃圾收集。


查看完整回答
反对 回复 2023-01-03
  • 1 回答
  • 0 关注
  • 79 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号