Go 中对单个计算成本高的资源的多个请求

为以下问题寻找更 Go-ish 的解决方案:


比如说,一个服务器有多个并行传入的请求,请求一个带有 key 的资源key。由于计算这个资源是昂贵的/耗时的,我们希望确保它只计算一次。有无数个可能的键。


一个天真的实现:


if hasCachedValue(key) {

   return cachedValue(key)

}

if somebodyElseWorkingOn(key) {

   waitUntilReady(key)

} else {

   buildCacheValue(key) // time consuming

}

return cachedValue(key)

到目前为止,我们已经使用 shared 解决了这个问题map[string]chan bool,其中第一个请求插入 chan key,当值准备好时,以下请求等待该 chan 的关闭。为了保护地图,我们使用了sync.Mutex,但我们感觉有一个更好、更 Go-ish 的解决方案。


白猪掌柜的
浏览 103回答 3
3回答

ITMISS

使用单程包。为组声明一个包级变量:var g singleflight.Group使用以下代码获取值:v, err, _ := g.Do(key, func() (interface{}, error) {    if !hasCachedValue(key) {        buildCacheValue(key)    }    return cachedValue(key), nil})if err != nil {    // handle error}x := v.(valueType) // assert to type returned by cachedValue// do something with x

慕田峪9158850

受到经常用于描述频道的 Ping Pong 示例的启发,我们坐下来尝试仅使用频道的方法。一个球保持关于正在生成的密钥的状态,并且球在请求之间传递一个共享通道:import "time"var table = make(chan map[string]chan bool)func keeper() {&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; ball := <- table&nbsp; &nbsp; &nbsp; &nbsp; table <- ball&nbsp; &nbsp; }}func getResource(key string) {&nbsp; &nbsp; // Take ball from table&nbsp; &nbsp; ball := <- table&nbsp; &nbsp; if wait, ok := ball[key]; ok{&nbsp; &nbsp; &nbsp; &nbsp; println("Somebody else working on " + key + ", waiting")&nbsp; &nbsp; &nbsp; &nbsp; table <- ball&nbsp; &nbsp; &nbsp; &nbsp; <- wait&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; println("I will build " + key)&nbsp; &nbsp; &nbsp; &nbsp; ball[key] = make(chan bool)&nbsp; &nbsp; &nbsp; &nbsp; // Throw away ball&nbsp; &nbsp; &nbsp; &nbsp; table <- ball&nbsp; &nbsp; &nbsp; &nbsp; // Building value&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Millisecond * 10)&nbsp; &nbsp; &nbsp; &nbsp; println("I built value for " + key + "!")&nbsp; &nbsp; &nbsp; &nbsp; // Clean up ball&nbsp; &nbsp; &nbsp; &nbsp; ball = <- table&nbsp; &nbsp; &nbsp; &nbsp; close(ball[key])&nbsp; &nbsp; &nbsp; &nbsp; delete(ball, key)&nbsp; &nbsp; &nbsp; &nbsp; table <- ball&nbsp; &nbsp; }&nbsp; &nbsp; println("Now value for " + key + " has been built")}func main(){&nbsp; &nbsp; go keeper()&nbsp; &nbsp; ball := make(map[string]chan bool)&nbsp; &nbsp; table <- ball&nbsp; &nbsp; key := "key"&nbsp; &nbsp; go getResource(key)&nbsp; &nbsp; go getResource(key)&nbsp; &nbsp; go getResource(key)&nbsp; &nbsp; time.Sleep(time.Second)}

浮云间

这是一个简单的代码,可以满足您的需求。我对其进行了测试,它可以正常工作。Go 比赛条件检查器没有检测到任何问题。type Cache struct {&nbsp; &nbsp; mtx sync.RWMutex&nbsp; &nbsp; m map[KeyType]*CacheValue}type CacheValue struct {&nbsp; &nbsp; val *ValueType&nbsp; &nbsp; mtx sync.Mutex}func NewCache() *Cache {&nbsp; &nbsp; return &Cache{m: make(map[KeyType]*CacheValue)}}func (c *Cache) Get(key KeyType) *ValueType {&nbsp; &nbsp; c.mtx.RLock()&nbsp; &nbsp; v := c.m[key]&nbsp; &nbsp; c.mtx.RUnlock()&nbsp; &nbsp; if v != nil {&nbsp; &nbsp; &nbsp; &nbsp; v.mtx.Lock()&nbsp; &nbsp; &nbsp; &nbsp; x := v.val&nbsp; &nbsp; &nbsp; &nbsp; v.mtx.Unlock()&nbsp; &nbsp; &nbsp; &nbsp; if x != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return x&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; if v == nil {&nbsp; &nbsp; &nbsp; &nbsp; c.mtx.Lock()&nbsp; &nbsp; &nbsp; &nbsp; v = c.m[key]&nbsp; &nbsp; &nbsp; &nbsp; if v == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v = &CacheValue{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c.m[key] = v&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; c.mtx.Unlock()&nbsp; &nbsp; }&nbsp; &nbsp; v.mtx.Lock()&nbsp; &nbsp; if v.val == nil {&nbsp; &nbsp; &nbsp; &nbsp; v.val = buildValue(key)&nbsp; &nbsp; }&nbsp; &nbsp; v.mtx.Unlock()&nbsp; &nbsp; return v.val}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go