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

考虑这段代码:


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 created

slice_test.go:70: Lenth: 0, Cap: 0, Pointer: 0x1019598

slice_test.go:86: Lenth: 0, Cap: 9728, Pointer: 0xc000500000

为什么只生成一次,地址却不同?为什么上限是 9728?像这样使用同一个切片有什么问题吗?


富国沪深
浏览 80回答 1
1回答

斯蒂芬大帝

为什么只生成一次,地址却不同?因为在第一个 for 循环中,您将其追加到它的容量之外,它的 inNew设置为零,并将 的结果重新分配给它append。详见:为什么append()会修改提供的切片?(见例子)像这样使用同一个切片有什么问题吗?可能有。当您使用 reslices时s = s[:0],您重置的是长度而不是容量。后备数组与之前的附加和重新分配操作中的相同。因此,如果您再次追加到s2,容量将足以不会导致重新分配,并且您最终将覆盖后备数组的第一个元素:一个示范性的例子:func TestSliceWithPool(t *testing.T) {&nbsp; &nbsp; var slicePool = sync.Pool{&nbsp; &nbsp; &nbsp; &nbsp; New: func() interface{} {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Log("Created")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; s := make([]interface{}, 0)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return s&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; }&nbsp; &nbsp; s, _ := slicePool.Get().([]interface{})&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; s = append(s, i)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(s)&nbsp;&nbsp; &nbsp; // ^ output: [0 1 2 3 4 5 6 7 8 9]&nbsp; &nbsp; s = s[:0]&nbsp; &nbsp; slicePool.Put(s)&nbsp; &nbsp; s2, _ := slicePool.Get().([]interface{})&nbsp; &nbsp; fmt.Println(s)&nbsp; &nbsp; // ^ output: []&nbsp; &nbsp; for i := 0; i < 5; i++ {&nbsp; &nbsp; &nbsp; &nbsp; s2 = append(s2, i*10)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(s2)&nbsp;&nbsp; &nbsp; // ^ output: [0 10 20 30 40]&nbsp; &nbsp; fmt.Println(s2[:10])&nbsp; &nbsp; // ^ output: [0 10 20 30 40 5 6 7 8 9]}这可能没问题,因为你现在有一个扩展容量的切片,不需要在追加时重新分配,但如果你的应用程序保持其他切片头指向相同的支持数组(如s和s2),从而防止缓冲区的垃圾收集。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go