15.sync.Pool
# 01.sync.Pool介绍
# 1、是什么
sync.Pool
适用于那些需要频繁创建和销毁的对象,特别是在短生命周期且对象分配与释放频率很高的场景
sync.Pool
是 Go 标准库中的一个并发安全的对象池,用于对象的复用与回收- 它可以降低 GC 压力并提高性能,特别是在高频率创建和销毁对象的场景中
- 当程序请求一个对象时,
sync.Pool
首先尝试从池中获取可复用的对象 - 如果没有可用的对象,则会调用用户提供的函数(如果设置了
New
字段)或返回nil
- 当对象不再使用时,程序可以将对象放回池中,供下次复用
# 2、原理
sync.Pool
是 sync 包下的一个组件,可以作为保存临时取还对象的一个“池子”Pool
结构体的定义为:Pool
中有两个定义的公共方法,分别是Put
- 向池中添加元素;Get
从池中获取元素,如果没有,则调用New
生成元素,如果New
未设置,则返回nil
type Pool struct {
noCopy noCopy
local unsafe.Pointer // 本地P缓存池指针
localSize uintptr // 本地P缓存池大小
// 当池中没有可能对象时
// 会调用 New 函数构造构造一个对象
New func() interface{}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3、怎么用
- 首先,
sync.Pool
是协程安全的,这对于使用者来说是极其方便的 - 使用前,设置好对象的
New
函数,用于在Pool
里没有缓存的对象时,创建一个 - 之后,在程序的任何地方、任何时候仅通过
Get()
、Put()
方法就可以取、还对象了
package main
import (
"fmt"
"sync"
)
var pool *sync.Pool
type Person struct {
Name string
}
func initPool() {
pool = &sync.Pool {
New: func() interface{} {
fmt.Println("Creating a new Person")
return new(Person)
},
}
}
func main() {
initPool()
p := pool.Get().(*Person) // get获取不到就会调用方法,创建一个
p.Name = "first"
pool.Put(p) // 使用 Put方法将对象放回 pool池子中
fmt.Println("Pool 里已有一个对象:&{first},调用 Get: ", pool.Get().(*Person))
fmt.Println("Pool 没有对象了,调用 Get: ", pool.Get().(*Person)) // 获取后再次获取就没有了,会再次创建
}
/*
Creating a new Person
Pool 里已有一个对象:&{first},调用 Get: &{first}
Creating a new Person
Pool 没有对象了,调用 Get: &{}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 4、实现细节
sync.Pool
在 Go 的 runtime 中有一套复杂的机制来处理对象的存取,主要包括
- 本地池:
- 每个 goroutine 维护一个私有池,存储在
sync.Pool
的local
结构中 - 私有池中的对象只能被当前 goroutine 访问,这种设计减少了锁的竞争
- 每个 goroutine 维护一个私有池,存储在
- 全局池:
- 当 goroutine 私有池为空时,
sync.Pool
会从全局池中获取对象 - 全局池中的对象是共享的,需要加锁来确保并发安全
- 当 goroutine 私有池为空时,
- GC 影响:
- Go 运行时会在垃圾回收时清空
sync.Pool
,即池中未被使用的对象会在 GC 时被回收 - 这意味着
sync.Pool
不是一个持久存储的缓存机制,而是一种临时对象复用技术
- Go 运行时会在垃圾回收时清空
# 5、sync.Pool
的局限性
- 非持久缓存:
- 由于 GC 会清空
sync.Pool
中的对象,因此它不能用于持久缓存 - 如果需要长时间存储对象,应考虑其他缓存机制(如
map
或LRU
缓存)
- 由于 GC 会清空
- 适合短生命周期对象:
sync.Pool
主要用于短生命周期的对象复用- 如果对象的生命周期较长,使用
sync.Pool
可能并不会带来显著的性能提升
- 不能精确控制对象数量:
sync.Pool
不提供对池中对象数量的控制,因此对象池中的对象数量完全依赖于程序的使用模式和 GC 的行为
# 02.gin中的Context pool
在
web
应用中,后台在处理用户的每条请求时都会为当前请求创建一个上下文环境Context
,用于存储请求信息及相应信息等Context
满足长生命周期的特点,且用户请求也是属于并发环境,所以对于线程安全的Pool
非常适合用来维护Context
的临时对象池Gin
在结构体Engine
中定义了一个pool
:
type Engine struct {
// ... 省略了其他字段
pool sync.Pool
}
1
2
3
4
2
3
4
- 初始化
engine
时定义了pool
的New
函数:
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
// allocateContext
func (engine *Engine) allocateContext() *Context {
// 构造新的上下文对象
return &Context{engine: engine}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- ServeHttp
// 从 pool 中获取,并转化为 *Context
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset() // reset
engine.handleHTTPRequest(c)
// 再扔回 pool 中
engine.pool.Put(c)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
上次更新: 2024/10/15 16:27:13