相关代码如下,我能确保的是,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 2024-05-09 14:33:00 +08:00 ![]() 建议把代码里无关的东西去掉, 然后搞一个带 main 函数的, 直接能运行的 demo. |
![]() | 2 Nazz 2024-05-09 14:35:25 +08:00 你把内存池当缓存用了? |
![]() | 3 afxcn OP 我自己怀疑问题可能出在: *applicatiOns= append(*applications, *application) 或 *o = (*o)[0:0] 这些代码里。 |
![]() | 5 voidmnwzp 2024-05-09 16:34:00 +08:00 via iPhone sync.pool 是为了减少在堆上分配内存以缓解 gc 的压力,一般我都是在频繁使用的对象或者比较大的对象才会考虑用 pool |
6 zzhaolei 2024-05-09 18:27:55 +08:00 挺好奇为什么 _applicationPool 要用下划线开头? |
7 dyllen 2024-05-09 20:22:42 +08:00 没怎么看明白,说的混乱是什么意思。 |
![]() | 8 afxcn OP |
![]() | 9 nagisaushio 2024-05-09 21:00:12 +08:00 你要保证放回 pool 时对象没有其他引用 |
![]() | 10 afxcn OP |
![]() | 12 Orlion 2024-05-10 10:34:47 +08:00 确定 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 2024-05-10 18:38:57 +08:00 你的 _applicationsPool 这里 New 这个 slice 的地方可以贴一下吗,感觉大概率是这里有点问题 |
![]() | 16 afxcn OP @cannotagreemore 您指的是这个:type ApplicationCollection []Application ? 还是这个: _applicatiOnsPool= sync.Pool{ New: func() any { applications := &ApplicationCollection{} return applications }} |
![]() | 17 cannotagreemore 2024-05-13 10:00:10 +08:00 这里 new 的时候显式指定一个最大的 Capacity 试一下 ``` _applicatiOnsPool= sync.Pool{ New: func() any { applications := &ApplicationCollection{} return applications }} ``` |
![]() | 18 afxcn OP @cannotagreemore 确实是 Capacity 的问题。 |
![]() | 19 cannotagreemore 2024-05-30 17:02:17 +08:00 @afxcn 这里是因为你前面的写法没有显示指定最大的 capacity ,默认的 cap=0 ,在 append 操作的时候会触发扩容。其实只需要使用 applications := make(ApplicationCollection, 0, 10) 来初始化就可以了,依然可以使用 append 去进行操作。 感觉这个场景确实没什么必要针对 slice 使用 sync.Pool 就是了。 |