我有一个使用sync.Pool的结构。
将此引用用作上下文值是否安全?
type User struct {
ID string
}
var userPool = sync.Pool{
New: func() interface{} {
return &User{}
},
}
func getUser() *User {
return userPool.Get().(*User)
}
func recycleUser(user *User) {
userPool.Put(user)
}用户结构是从中间件的池中检索的。
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// get user from pool
user := getUser()
// user must be recycled
ctx := context.WithValue(r.Context(), "user", user)
}
}并在处理器中回收。
func getUser(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*User)
// TODO: do something with user
// put user struct back into pool
recycleUser(user)
}编辑:
我的问题更多的是关于上下文如何处理指向我的对象的指针。它会复制吗?在上下文中使用非原语对象是否安全?
发布于 2017-08-26 15:30:13
有三点需要说明:
sync.Pool的用法
您对池的使用符合预期。来自golang Documentation
池可以安全地同时供多个goroutine使用。
检索用户
getUser使用func (p *Pool) Get() interface{}从池中删除返回的项,然后您可以随心所欲地处理该值。在您的例子中,这是一个*User。关联的ressource是否可以安全使用,取决于您在程序的其余部分中对这些值的使用。
回收用户
根据您的环境,在处理程序中调用recycleUser具有潜在的危险。
有什么可能出错?
在recycleUser将您的*User返回到池中之后,它可以立即被另一个goroutine检索和使用。但同时,*User仍然存储在与请求相关的上下文中。因此,这取决于中间件的任何函数是否也使用上下文中的*User,以及它们是否存储*User值。或者,如果您稍后在使用*User值的recycleUser之后添加一些代码。在recycleUser调用之后,所有这些用法都可能操作错误的用户,而这个用户已经被一些不同的请求使用了。
如何解决这些问题
- if you retrieve the user in middleware functions, don't store it
- if you retrieve the user in middleware functions, don't use it after the call to the next handler in the chain.
- don't call `recycleUser` in middleware functions
- retrieve the user in the leaf handler (last handler in the chain, doesn't calls any further handlers propagating the context and thus the `*User`) and use `defer recycleUser(user)` to put back the `*User` in the pool and make it impossible that later added code uses `*User` after the call to `recycleUser(user)`.
- in the leaf handler, don't use `*User` in code which is called by some `defer` usage in the leaf handler, other than `recycleUser`.
*User放在某个结构中,并且只对一个请求使用该结构,并将其标记为空,一旦*User被放回到池中。如果一个空的结构被访问,那么让该结构的所有访问方法检查是否为空和死机/日志/任何东西。在上下文中放置一个指向该结构的指针。但这可能是没有意义的,因为您现在为每个请求分配该结构,而不是您试图避免的User。一个次要的评论
您可能想让您的中间件函数读起来像这样:
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// get user from pool
user := getUser()
// user must be recycled
ctx := context.WithValue(r.Context(), "user", user)
// probably do something more
next(w, r.WithContext(ctx))
}
}https://stackoverflow.com/questions/45891769
复制相似问题