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怎么正确缓存一个列表?
建议把代码里无关的东西去掉, 然后搞一个带 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 里面还是少见下划线开头的代码
您指的是这个: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 := &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
来缓存一个列表(例如,一个切片),你可以按照以下步骤进行:
-
创建
sync.Pool
实例: 首先,你需要创建一个sync.Pool
实例。这个实例将用于存储你的切片类型。var pool = sync.Pool{ New: func() interface{} { return make([]int, 0, 10) // 初始化切片,指定容量 }, }
-
获取和放回对象: 使用
Get
方法从池中获取切片,使用Put
方法将切片放回池中。// 获取切片 list := pool.Get().([]int) // 使用切片 list = append(list, 1, 2, 3) // 完成使用后放回池中 pool.Put(list[:0]) // 清空切片内容但保留底层数组,避免内存泄漏
-
注意事项:
- 使用
Put
时,最好清空切片的内容(如list[:0]
),避免缓存中留下带有旧数据的切片。 sync.Pool
中的对象可能会在任何时候被回收,因此不能依赖它存储必须持久存在的数据。- 适用于短生命周期对象的临时缓存,对于长生命周期对象可能效果不佳。
- 使用
通过上述方法,你可以有效地利用 sync.Pool
来缓存和重用切片,提高程序的性能。