将缓存添加到go函数中,就像它是静态成员一样

zkure5ic  于 12个月前  发布在  Go
关注(0)|答案(4)|浏览(118)

假设我有一个昂贵的功能

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中模拟静态成员缓存的好方法是什么?

oprakyz7

oprakyz71#

您可以使用闭包,并让闭包管理该高速缓存。

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()

字符串
并在代码中使用wrapped函数。您可以尝试here
如果你希望它是可重用的,将签名改为InitExpensiveFuncWithCache(func(int) int),这样它就可以接受一个函数作为参数。

j8ag8udp

j8ag8udp2#

如果这个缓存将在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){
        
    })
}

字符串

ipakzgxi

ipakzgxi3#

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)

yx2lnoni

yx2lnoni4#

你可以写一个生成器来创建一个cacheMap。
就像这样:

type cachedFn[K any, V any] struct {
        cacheMap       sync.Map
        getFunc        func(K) (V, error)
    }
    
    func NewCacheFn[K any, V any](getFunc func(K) (V, error)) *cachedFn[K, V] {
        return &cachedFn[K, V]{getFunc: getFunc}
    }
    func (c *cachedFn[K, V]) Get(key K) (V, error) {
          // you logic
    }

字符串
使用这个生成器像这样:

ExpensiveFuncWithCache := NewCacheFn(ExpensiveFunc).Get
    ExpensiveFuncWithCache(arg)


下面是一个例子:https://go.dev/play/p/guzOWRKi-yp
下一篇:github test

相关问题