首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用sync.Pool引用作为上下文值安全吗?

使用sync.Pool引用作为上下文值安全吗?
EN

Stack Overflow用户
提问于 2017-08-26 11:51:09
回答 1查看 470关注 0票数 2

我有一个使用sync.Pool的结构。

将此引用用作上下文值是否安全?

代码语言:javascript
复制
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)
}

用户结构是从中间件的池中检索的。

代码语言:javascript
复制
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)
    }
}

并在处理器中回收。

代码语言:javascript
复制
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)
}

编辑:

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

EN

回答 1

Stack Overflow用户

发布于 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调用之后,所有这些用法都可能操作错误的用户,而这个用户已经被一些不同的请求使用了。

如何解决这些问题

  1. 编码约定

代码语言:javascript
复制
- 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`.

  1. 通过技术手段确保编码约定。我在这里唯一的想法是将*User放在某个结构中,并且只对一个请求使用该结构,并将其标记为空,一旦*User被放回到池中。如果一个空的结构被访问,那么让该结构的所有访问方法检查是否为空和死机/日志/任何东西。在上下文中放置一个指向该结构的指针。但这可能是没有意义的,因为您现在为每个请求分配该结构,而不是您试图避免的User

一个次要的评论

您可能想让您的中间件函数读起来像这样:

代码语言:javascript
复制
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))
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45891769

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档