请教, sync.Pool 怎么正确缓存一个列表?

47 天前
 afxcn

相关代码如下,我能确保的是,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 tableName="applications"
type Application struct {
	// @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)
}
1695 次点击
所在节点    Go 编程语言
19 条回复
MoYi123
47 天前
建议把代码里无关的东西去掉, 然后搞一个带 main 函数的, 直接能运行的 demo.
Nazz
47 天前
你把内存池当缓存用了?
afxcn
47 天前
我自己怀疑问题可能出在:

*applications = append(*applications, *application)



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

这些代码里。
afxcn
47 天前
@Nazz 缓存的是对象,不是数据,主要是在并发高的时候可以少占点内存吧。
voidmnwzp
47 天前
sync.pool 是为了减少在堆上分配内存以缓解 gc 的压力,一般我都是在频繁使用的对象或者比较大的对象才会考虑用 pool
zzhaolei
47 天前
挺好奇为什么 _applicationPool 要用下划线开头?
dyllen
47 天前
没怎么看明白,说的混乱是什么意思。
afxcn
47 天前
@dyllen 返回的数据是错误的,就是有些数据丢失,有些重复了。

@zzhaolei 个人习惯,只要是全局变量都加个下划线 。
nagisaushio
47 天前
你要保证放回 pool 时对象没有其他引用
afxcn
47 天前
@nagisaushio 也就是说对象不能自己 Release 自己,因为自己还指向自己?

如果是这样的话,我的整个写法都是错误的了。得好好验证。
zzhaolei
47 天前
@afxcn 不导出的加一个下划线还行,能区分,导出的全局变量你加吗?不加就不统一,加了就不能导出
Orlion
47 天前
确定 ApplicationCollection.Release()调用时机是正确的吗? Release()之后是否还使用到了 ApplicationCollection 或者里面的元素?建议将 Release()的调用代码也发出来。
afxcn
47 天前
@Orlion 用的是这个库: https://github.com/gostartkit/web/blob/master/application.go

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

```go
if rel, ok := val.(IRelease); ok {
rel.Release()
val = nil
}
```
kfish
47 天前
@zzhaolei 写其他语言带过来的风格吧, 不过 Go 里面还是少见下划线开头的代码
cannotagreemore
46 天前
你的 _applicationsPool 这里 New 这个 slice 的地方可以贴一下吗,感觉大概率是这里有点问题
afxcn
46 天前
@cannotagreemore 您指的是这个:type ApplicationCollection []Application ?

还是这个:

_applicationsPool = sync.Pool{
New: func() any {
applications := &ApplicationCollection{}
return applications
}}
cannotagreemore
44 天前
这里 new 的时候显式指定一个最大的 Capacity 试一下
```
_applicationsPool = sync.Pool{
New: func() any {
applications := &ApplicationCollection{}
return applications
}}
```
afxcn
27 天前
@cannotagreemore 确实是 Capacity 的问题。
cannotagreemore
26 天前
@afxcn 这里是因为你前面的写法没有显示指定最大的 capacity ,默认的 cap=0 ,在 append 操作的时候会触发扩容。其实只需要使用 applications := make(ApplicationCollection, 0, 10) 来初始化就可以了,依然可以使用 append 去进行操作。
感觉这个场景确实没什么必要针对 slice 使用 sync.Pool 就是了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1039113

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX