使用sync.Pool引用作为上下文值是否安全?

时间:2017-08-26 03:51:09

标签: http go synchronization

我的结构使用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)
}

编辑:

我的问题更多的是关于上下文如何处理指向我的对象的指针。它是否复制?在上下文中使用非原始对象是否安全?

1 个答案:

答案 0 :(得分:0)

要做三点:

sync.Pool

的用法

您对游泳池的使用是按预期的。来自golang Documentation

  

池可以安全地同时使用多个goroutine。

检索用户

getUser使用func (p *Pool) Get() interface{}从池中删除退回的项目,之后您可以随心所欲地做任何事情 有这个价值。在你的情况下,那是一个*User。是否相关 ressource可以安全使用,取决于你在程序其余部分中对这些值的使用。

回收用户

根据您的环境,在管理员中调用recycleUser可能会有危险。

可能出现什么问题?

recycleUser*User返回池后,可以立即检索并使用其他goroutine。但与此同时,*User仍存储在与请求相关联的上下文中。因此,取决于中间件的任何功能是否也使用上下文中的*User,并且它们存储*User值。或者,如果您稍后在使用recycleUser值的*User之后添加一些代码。在recycleUser调用之后,所有这些用法可能都在错误的用户上运行,这已被某些不同的请求使用。

如何解决这些问题

  1. 编码惯例

    • 如果您在中间件功能中检索用户,请不要将其存储
    • 如果您在中间件功能中检索用户,请在调用链中的下一个处理程序后不要使用它。
    • 不要在中间件功能中调用recycleUser
    • 在叶子处理程序中检索用户(链中的最后一个处理程序,不会调用传播上下文的任何其他处理程序,因此调用*User)并使用defer recycleUser(user)来放回{池中的{1}}使得以后添加的代码无法在调用*User后使用*User
    • 叶子处理程序中的
    • ,不要在代码中使用recycleUser(user),该代码由叶子处理程序中的某些*User用法调用,而不是defer
  2. 通过技术手段确保编码惯例。我唯一的想法是将recycleUser置于某个结构中,并仅将该结构用于一个请求,并将*User放回池中后将其标记为空。如果访问空结构,则使该结构的所有访问方法检查空白和panic / log / whatever。在上下文中放置一个指向该结构的指针。但这可能毫无意义,因为您现在为每个请求分配该结构,而不是*User,这可能是您试图避免的。

  3. 次要评论

    您可能意味着您的中间件功能如下:

    User