Golang Go语言中,请教,sync.Pool怎么正确缓存一个列表?

相关代码如下,我能确保的是,Create 和 Release 是成对出现的;通过下面代码第一次拿到的数据是对的,多次就会出现混乱了。

func (r *ApplicationRepository) GetApplications(filter string, orderBy string, page int, pageSize int) (*model.ApplicationCollection, error) {
var sqlx strings.Builder
var args []any

sqlx.WriteString("SELECT `id`, `application_name`, `created_at`, `updated_at` ")
sqlx.WriteString("FROM `applications` ")
sqlx.WriteString("WHERE `status` >= 0 ")

if filter != "" {
	sqlx.WriteString("AND ")
	if err := utils.SqlFilter(filter, &sqlx, &args, "", r.tryParse); err != nil {
		return nil, err
	}
	sqlx.WriteString(" ")
}

if orderBy != "" {
	sqlx.WriteString("ORDER BY ")
	if err := utils.SqlOrderBy(orderBy, &sqlx, "", r.tryParseKey); err != nil {
		return nil, err
	}
	sqlx.WriteString(" ")
}

sqlx.WriteString("limit ? offset ?")

if pageSize > _maxPageSize {
	pageSize = _maxPageSize
} else if pageSize <= 0 {
	pageSize = _pageSize
}

offset := 0

if page > 1 {
	offset = (page - 1) * pageSize
}

args = append(args, pageSize, offset)

rows, err := query(sqlx.String(), args...)

if err != nil {
	return nil, err
}

defer rows.Close()

applications := model.CreateApplicationCollection()

for rows.Next() {

	application := model.CreateApplication()

	err := rows.Scan(&application.ID, &application.ApplicationName, &application.CreatedAt, &application.UpdatedAt)

	if err != nil {
		return nil, err
	}

	*applications = append(*applications, *application)
}

return applications, rows.Err()

}

package model

import (
	"time"
)

// Application model
// [@Entity](/user/Entity) tableName="applications"
type Application struct {
	// [@PrimaryKey](/user/PrimaryKey)
	ID uint64 `json:"id"`
	ApplicationName string `json:"applicationName"`
	CreatedAt *time.Time `json:"createdAt"`
	UpdatedAt *time.Time `json:"updatedAt"`
}

// ApplicationCollection Application list
type ApplicationCollection []Application
package model

import "sync"

var (
	_applicationPool = sync.Pool{
		New: func() any {
			application := &Application{}
			return application
		}}

	_applicationsPool = sync.Pool{
		New: func() any {
			applications := &ApplicationCollection{}
			return applications
		}}
)

// CreateApplication return *Application
func CreateApplication() *Application {

	application := _applicationPool.Get().(*Application)

	return application
}

func (o *Application) initial() {
	o.ID = 0
	o.ApplicationName = ""
	o.CreatedAt = nil
	o.UpdatedAt = nil
}

func (o *Application) Release() {
	o.initial()
	_applicationPool.Put(o)
}

// CreateApplicationCollection return *ApplicationCollection
func CreateApplicationCollection() *ApplicationCollection {

	applicationCollection := _applicationsPool.Get().(*ApplicationCollection)

	return applicationCollection
}

func (o *ApplicationCollection) initial() {
	*o = (*o)[0:0]
}

func (o *ApplicationCollection) Release() {
	for i := 0; i < o.Len(); i++ {
		(*o)[i].Release()
	}
	o.initial()
	_applicationsPool.Put(o)
}

Golang Go语言中,请教,sync.Pool怎么正确缓存一个列表?
20 回复

建议把代码里无关的东西去掉, 然后搞一个带 main 函数的, 直接能运行的 demo.

更多关于Golang Go语言中,请教,sync.Pool怎么正确缓存一个列表?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你把内存池当缓存用了?

我自己怀疑问题可能出在:

*applications = append(*applications, *application)



*o = (*o)[0:0]

这些代码里。

缓存的是对象,不是数据,主要是在并发高的时候可以少占点内存吧。

sync.pool 是为了减少在堆上分配内存以缓解 gc 的压力,一般我都是在频繁使用的对象或者比较大的对象才会考虑用 pool

挺好奇为什么 _applicationPool 要用下划线开头?

没怎么看明白,说的混乱是什么意思。

返回的数据是错误的,就是有些数据丢失,有些重复了。

个人习惯,只要是全局变量都加个下划线 。

你要保证放回 pool 时对象没有其他引用

也就是说对象不能自己 Release 自己,因为自己还指向自己?

如果是这样的话,我的整个写法都是错误的了。得好好验证。

不导出的加一个下划线还行,能区分,导出的全局变量你加吗?不加就不统一,加了就不能导出

确定 ApplicationCollection.Release()调用时机是正确的吗? Release()之后是否还使用到了 ApplicationCollection 或者里面的元素?建议将 Release()的调用代码也发出来。

用的是这个库: https://github.com/gostartkit/web/blob/master/application.go

主要代码是这个 #218 - #221 行

go<br> if rel, ok := val.(IRelease); ok {<br> rel.Release()<br> val = nil<br> }<br>

写其他语言带过来的风格吧, 不过 Go 里面还是少见下划线开头的代码

你的 _applicationsPool 这里 New 这个 slice 的地方可以贴一下吗,感觉大概率是这里有点问题

您指的是这个:type ApplicationCollection []Application ?

还是这个:

_applicationsPool = sync.Pool{
New: func() any {
applications := &ApplicationCollection{}
return applications
}}

这里 new 的时候显式指定一个最大的 Capacity 试一下
<br>_applicationsPool = sync.Pool{<br> New: func() any {<br> applications := &amp;ApplicationCollection{}<br> return applications<br>}}<br>

确实是 Capacity 的问题。

这里是因为你前面的写法没有显示指定最大的 capacity ,默认的 cap=0 ,在 append 操作的时候会触发扩容。其实只需要使用 applications := make(ApplicationCollection, 0, 10) 来初始化就可以了,依然可以使用 append 去进行操作。
感觉这个场景确实没什么必要针对 slice 使用 sync.Pool 就是了。

在Go语言中,sync.Pool 是一个非常有用的工具,用于临时缓存和重用对象,以减少垃圾回收的开销。要正确地使用 sync.Pool 来缓存一个列表(例如,一个切片),你可以按照以下步骤进行:

  1. 创建 sync.Pool 实例: 首先,你需要创建一个 sync.Pool 实例。这个实例将用于存储你的切片类型。

    var pool = sync.Pool{
        New: func() interface{} {
            return make([]int, 0, 10) // 初始化切片,指定容量
        },
    }
  2. 获取和放回对象: 使用 Get 方法从池中获取切片,使用 Put 方法将切片放回池中。

    // 获取切片
    list := pool.Get().([]int)
    
    // 使用切片
    list = append(list, 1, 2, 3)
    
    // 完成使用后放回池中
    pool.Put(list[:0]) // 清空切片内容但保留底层数组,避免内存泄漏
  3. 注意事项

    • 使用 Put 时,最好清空切片的内容(如 list[:0]),避免缓存中留下带有旧数据的切片。
    • sync.Pool 中的对象可能会在任何时候被回收,因此不能依赖它存储必须持久存在的数据。
    • 适用于短生命周期对象的临时缓存,对于长生命周期对象可能效果不佳。

通过上述方法,你可以有效地利用 sync.Pool 来缓存和重用切片,提高程序的性能。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!