当前位置: 首页 > 工具软件 > 豆豆Pool > 使用案例 >

Pool池对象-sync.Pool

濮赤岩
2023-12-01

对于一些对象,为了优化性能,节省每次使用都创建新对象所带来的内存开销,可以使用池化技术,预先创建好一些对象放入池中,使用时从池中获取,使用完再放回池中。这样就减少了对象创建所带来的开销。

在go中,原生的池化数据结构为sync.Pool, 有三个方法:

  • New字段为一个方法,定义为func() interface{}, 在新建Pool时,定义好New字段,以供后续从池中获取对象时,如果当前池中无对象,则使用此方法来新建对象。
  • Get()方法:从池中获取对象
  • Put(x interface{})方法:将对象放入池中

使用样例:

	conPool := sync.Pool{
		New: func() interface{} { return &Con{age: 20} },
	}

	con := conPool.Get().(*Con)
	fmt.Printf("con.age: %v\n", con.Age())
	
	conPool.Put(con)

sync.Pool用于保存一组可独立访问的临时对象,注意加粗的临时,说明它池化的对象在未来某个时候会被
毫无征兆的移除掉。

go中sync.Pool的特点:

  • 池中对象数量没有最少最多限制,只要存和取,都可以成功
  • 池中的对象会被随机回收,当程序GC时,可能会回收池中对象
  • 存和取可以并发使用,本身就是线程安全的
  • 池中的对象是bytes.Buffer时,如果buffer的cap随着每次使用而变化,尽管每次使用完后buffer.Reset()清空了长度,但底层的cap仍然很大,会造成内存泄露

使用sync.Pool的注意点:

  • 因为池化对象可能会被垃圾回收,因此对于tcp链接、数据库链接等一下长连接是不合适的
  • 如果要限制池中对象的最多最少限制,需要自己在这基础上做修改
  • 如果池中对象有bytes.Buffer类型, 每次放回池之前对cap做判断,如果太大就不再放回池中,避免内存泄露

连接池

Pool 的另一个很常用的一个场景就是保持 TCP 的连接。
(事实上,很少用sync.Pool去池化连接对象,因为sync.Pool会无通知的将某个连接回收,因此
会其他方法来池化对象)

  • 标准库中的 http client 池
    http.Client 实现连接池的代码是在 Transport 类型中,它使用 idleConn 保存持久化的可重用的长连接:
  • fatih/pool的tcp连接池
    它的 Pool 是通过 Channel 实现的,空闲的连接放入到 Channel 中,这也是 Channel 的一个应用场景:
  • 数据库连接池
    通过 MaxOpenConns 和 MaxIdleConns 控制最大的连接数和最大的 idle 的连接数。
  • Memcached Client 连接池
    gomemcache是他使用 Go 开发的 Memchaced 的客户端,其中也用了连接池的方式池化 Memcached 的连接

Worker Pool

当使用并发任务时,我们常常建立一个任务池,控制并发时最少最多的goroutine数量。

总结

Pool是一个通用的概念,当程序中有很多需要重复创建的对象,可以创建池类对象。

 类似资料: