相关代码如下,我能确保的是,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)
}
我们目前暂时的解决办法是去掉_applicationsPool,不再缓存列表对象。
代码如下:
package model
import "sync"
var (
_applicationPool = sync.Pool{
New: func() any {
application := &Application{}
return application
}}
)
// 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 {
applications := &ApplicationCollection{}
return applications
}
func (o *ApplicationCollection) Release() {
for i := 0; i < o.Len(); i++ {
(*o)[i].Release()
}
}
append 的 问题,换成make就没问题了。
错误的写法:
applications := &ApplicationCollection{}
*applications = append(*applications, *application)
正确的做法:
applications := make(ApplicationCollection, 0, 10)
applications[i] = application
不过在这种场景下使用sync.Pool带来的好处比不上它带来的麻烦,除非是访问并发很高,并且cpu和内存很贵的环境才有必要。
1
MoYi123 216 天前 2
建议把代码里无关的东西去掉, 然后搞一个带 main 函数的, 直接能运行的 demo.
|
2
Nazz 216 天前
你把内存池当缓存用了?
|
3
afxcn OP 我自己怀疑问题可能出在:
*applications = append(*applications, *application) 或 *o = (*o)[0:0] 这些代码里。 |
5
voidmnwzp 216 天前 via iPhone
sync.pool 是为了减少在堆上分配内存以缓解 gc 的压力,一般我都是在频繁使用的对象或者比较大的对象才会考虑用 pool
|
6
zzhaolei 216 天前
挺好奇为什么 _applicationPool 要用下划线开头?
|
7
dyllen 216 天前
没怎么看明白,说的混乱是什么意思。
|
9
nagisaushio 216 天前
你要保证放回 pool 时对象没有其他引用
|
10
afxcn OP |
12
Orlion 216 天前
确定 ApplicationCollection.Release()调用时机是正确的吗? Release()之后是否还使用到了 ApplicationCollection 或者里面的元素?建议将 Release()的调用代码也发出来。
|
13
afxcn OP @Orlion 用的是这个库: https://github.com/gostartkit/web/blob/master/application.go
主要代码是这个 #218 - #221 行 ```go if rel, ok := val.(IRelease); ok { rel.Release() val = nil } ``` |
15
cannotagreemore 215 天前
你的 _applicationsPool 这里 New 这个 slice 的地方可以贴一下吗,感觉大概率是这里有点问题
|
16
afxcn OP @cannotagreemore 您指的是这个:type ApplicationCollection []Application ?
还是这个: _applicationsPool = sync.Pool{ New: func() any { applications := &ApplicationCollection{} return applications }} |
17
cannotagreemore 213 天前
这里 new 的时候显式指定一个最大的 Capacity 试一下
``` _applicationsPool = sync.Pool{ New: func() any { applications := &ApplicationCollection{} return applications }} ``` |
18
afxcn OP @cannotagreemore 确实是 Capacity 的问题。
|
19
cannotagreemore 195 天前
@afxcn 这里是因为你前面的写法没有显示指定最大的 capacity ,默认的 cap=0 ,在 append 操作的时候会触发扩容。其实只需要使用 applications := make(ApplicationCollection, 0, 10) 来初始化就可以了,依然可以使用 append 去进行操作。
感觉这个场景确实没什么必要针对 slice 使用 sync.Pool 就是了。 |