Go语言如何为函数模拟静态成员缓存 实现高开销函数结果复用
Go语言实现函数内置静态缓存的方案
Go没有原生的函数静态成员语法,但可以通过闭包特性模拟该效果,缓存完全封装在函数内部,外部无感知,同时也可以通过泛型实现通用缓存逻辑,复用于所有同类函数。
方案1:闭包实现单函数内置缓存
利用闭包捕获的变量的生命周期和闭包函数绑定的特性,实现类似静态成员的效果:
import "sync" // veryExpensiveFunctionCached 与原函数签名完全一致,外部调用无需感知缓存 var veryExpensiveFunctionCached = func() func(int) int { // 缓存变量仅闭包内部可访问,不会被外部修改 cache := make(map[int]int) // 互斥锁保证并发调用安全,单协程场景可删除 var mu sync.Mutex return func(p int) int { mu.Lock() defer mu.Unlock() // 命中缓存直接返回 if val, ok := cache[p]; ok { return val } // 未命中则调用原函数计算后存入缓存 res := veryExpensiveFunction(p) cache[p] = res return res } }()
调用时和原函数用法完全一致:veryExpensiveFunctionCached(10),不需要额外传缓存参数。
方案2:泛型实现通用缓存装饰器(Go 1.18+支持)
如果需要给多个同类函数加缓存,可以用泛型封装通用装饰器,一次实现多处复用:
import "sync" // CacheDecorator 通用缓存装饰器,入参为 func(K) V 类型的函数,返回带缓存的同签名函数 // K 为参数类型,要求是可比较类型(可以作为map的key) // V 为返回值类型 func CacheDecorator[K comparable, V any](fn func(K) V) func(K) V { cache := make(map[K]V) var mu sync.Mutex return func(k K) V { mu.Lock() defer mu.Unlock() if v, ok := cache[k]; ok { return v } v := fn(k) cache[k] = v return v } }
使用方式非常简单,给需要加缓存的函数套一层装饰器即可:
// 给第一个高开销函数加缓存 var veryExpensiveFunctionCached = CacheDecorator(veryExpensiveFunction) // 给第二个高开销函数加缓存 var veryExpensiveFunction2Cached = CacheDecorator(veryExpensiveFunction2)
注意事项
- 上述实现为永久缓存,如果参数取值范围极大,会有内存占用过高的风险,可以将map替换为带LRU淘汰逻辑的缓存结构优化
- 如果函数有多个入参,可将多个入参封装为结构体作为map的key,只要结构体所有字段都是可比较类型即可正常使用
- 如果业务场景允许缓存过期,可以给缓存值加上过期时间戳,返回前校验是否过期即可
内容的提问来源于stack exchange,提问作者Beginner
相关产品推荐
相关产品推荐

