将缓存添加到 go 函数,就好像它是静态成员一样
Add a cache to a go function as if it were a static member
说我有一个昂贵的功能
func veryExpensiveFunction(int) int
而且这个函数被同一个号码调用了很多次。
是否有一种好方法允许此函数存储以前的结果,以便在再次调用该函数时使用,甚至可以为 veryExpensiveFunction2 重用?
显然,可以添加一个参数
func veryExpensiveFunctionCached(p int, cache map[int]int) int {
if val, ok := cache[p]; ok {
return val
}
result := veryExpensiveFunction(p)
cache[p] = result
return result
}
但现在我必须在某个地方创建缓存,而我并不关心它。如果可能的话,我宁愿将它作为“静态函数成员”。
在go中模拟静态成员缓存有什么好方法?
你可以使用闭包;并让闭包管理缓存。
func InitExpensiveFuncWithCache() func(p int) int {
var cache = make(map[int]int)
return func(p int) int {
if ret, ok := cache[p]; ok {
fmt.Println("from cache")
return ret
}
// expensive computation
time.Sleep(1 * time.Second)
r := p * 2
cache[p] = r
return r
}
}
func main() {
ExpensiveFuncWithCache := InitExpensiveFuncWithCache()
fmt.Println(ExpensiveFuncWithCache(2))
fmt.Println(ExpensiveFuncWithCache(2))
}
output:
4
from cache
4
veryExpensiveFunctionCached := InitExpensiveFuncWithCache()
并在您的代码中使用包装函数。
你可以试试here.
如果您希望它可重复使用,请将签名更改为 InitExpensiveFuncWithCache(func(int) int)
,以便它接受一个函数作为参数。将它包裹在闭包中,用它代替昂贵的计算部分。
如果此缓存将在 http 处理程序中使用,您需要注意同步。在 Go 标准库中,每个 http 请求都在专用的 goroutine 中处理,此时我们处于并发和竞争条件的领域。我建议 RWMutex 以确保数据一致性。
至于缓存注入,您可以在创建 http 处理程序的函数中注入它。
这是一个原型
type Cache struct {
store map[int]int
mux sync.RWMutex
}
func NewCache() *Cache {
return &Cache{make(map[int]int), sync.RWMutex{}}
}
func (c *Cache) Set(id, value int) {
c.mux.Lock()
c.store[id] = id
c.mux.Unlock()
}
func (c *Cache) Get(id int) (int, error) {
c.mux.RLock()
v, ok := c.store[id]
c.mux.RUnlock()
if !ok {
return -1, errors.New("a value with given key not found")
}
return v, nil
}
func handleComplexOperation(c *Cache) http.HandlerFunc {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request){
})
}
Go 标准库使用以下样式来提供“静态”函数(例如 flag.CommandLine),但它利用了底层状态:
// "static" function is just a wrapper
func Lookup(p int) int { return expCache.Lookup(p) }
var expCache = NewCache()
func newCache() *CacheExpensive { return &CacheExpensive{cache: make(map[int]int)} }
type CacheExpensive struct {
l sync.RWMutex // lock for concurrent access
cache map[int]int
}
func (c *CacheExpensive) Lookup(p int) int { /*...*/ }
这种设计模式不仅允许简单的一次性使用,还允许隔离使用:
var (
userX = NewCache()
userY = NewCache()
)
userX.Lookup(12)
userY.Lookup(42)
说我有一个昂贵的功能
func veryExpensiveFunction(int) int
而且这个函数被同一个号码调用了很多次。
是否有一种好方法允许此函数存储以前的结果,以便在再次调用该函数时使用,甚至可以为 veryExpensiveFunction2 重用?
显然,可以添加一个参数
func veryExpensiveFunctionCached(p int, cache map[int]int) int {
if val, ok := cache[p]; ok {
return val
}
result := veryExpensiveFunction(p)
cache[p] = result
return result
}
但现在我必须在某个地方创建缓存,而我并不关心它。如果可能的话,我宁愿将它作为“静态函数成员”。
在go中模拟静态成员缓存有什么好方法?
你可以使用闭包;并让闭包管理缓存。
func InitExpensiveFuncWithCache() func(p int) int {
var cache = make(map[int]int)
return func(p int) int {
if ret, ok := cache[p]; ok {
fmt.Println("from cache")
return ret
}
// expensive computation
time.Sleep(1 * time.Second)
r := p * 2
cache[p] = r
return r
}
}
func main() {
ExpensiveFuncWithCache := InitExpensiveFuncWithCache()
fmt.Println(ExpensiveFuncWithCache(2))
fmt.Println(ExpensiveFuncWithCache(2))
}
output:
4
from cache
4
veryExpensiveFunctionCached := InitExpensiveFuncWithCache()
并在您的代码中使用包装函数。 你可以试试here.
如果您希望它可重复使用,请将签名更改为 InitExpensiveFuncWithCache(func(int) int)
,以便它接受一个函数作为参数。将它包裹在闭包中,用它代替昂贵的计算部分。
如果此缓存将在 http 处理程序中使用,您需要注意同步。在 Go 标准库中,每个 http 请求都在专用的 goroutine 中处理,此时我们处于并发和竞争条件的领域。我建议 RWMutex 以确保数据一致性。
至于缓存注入,您可以在创建 http 处理程序的函数中注入它。 这是一个原型
type Cache struct {
store map[int]int
mux sync.RWMutex
}
func NewCache() *Cache {
return &Cache{make(map[int]int), sync.RWMutex{}}
}
func (c *Cache) Set(id, value int) {
c.mux.Lock()
c.store[id] = id
c.mux.Unlock()
}
func (c *Cache) Get(id int) (int, error) {
c.mux.RLock()
v, ok := c.store[id]
c.mux.RUnlock()
if !ok {
return -1, errors.New("a value with given key not found")
}
return v, nil
}
func handleComplexOperation(c *Cache) http.HandlerFunc {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request){
})
}
Go 标准库使用以下样式来提供“静态”函数(例如 flag.CommandLine),但它利用了底层状态:
// "static" function is just a wrapper
func Lookup(p int) int { return expCache.Lookup(p) }
var expCache = NewCache()
func newCache() *CacheExpensive { return &CacheExpensive{cache: make(map[int]int)} }
type CacheExpensive struct {
l sync.RWMutex // lock for concurrent access
cache map[int]int
}
func (c *CacheExpensive) Lookup(p int) int { /*...*/ }
这种设计模式不仅允许简单的一次性使用,还允许隔离使用:
var (
userX = NewCache()
userY = NewCache()
)
userX.Lookup(12)
userY.Lookup(42)