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

寻找更 Go 式的解决方案来解决以下问题:


假设服务器有多个并行传入请求,要求使用 key 的资源key。由于计算此资源非常昂贵/耗时,因此我们希望确保仅计算一次。有无限多个可能的键。


一种简单的实现:


if hasCachedValue(key) {

   return cachedValue(key)

}

if somebodyElseWorkingOn(key) {

   waitUntilReady(key)

} else {

   buildCacheValue(key) // time consuming

}

return cachedValue(key)

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


翻翻过去那场雪
浏览 152回答 3
3回答

波斯汪

使用单程套餐。为该组声明一个包级变量: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

肥皂起泡泡

这是一个简单的代码,可以完成您想要的操作。我测试了它,它工作没有问题。Go 竞争条件检查器没有检测到任何问题。type Cache struct {    mtx sync.RWMutex    m map[KeyType]*CacheValue}type CacheValue struct {    val *ValueType    mtx sync.Mutex}func NewCache() *Cache {    return &Cache{m: make(map[KeyType]*CacheValue)}}func (c *Cache) Get(key KeyType) *ValueType {    c.mtx.RLock()    v := c.m[key]    c.mtx.RUnlock()    if v != nil {        v.mtx.Lock()        x := v.val        v.mtx.Unlock()        if x != nil {            return x        }    }    if v == nil {        c.mtx.Lock()        v = c.m[key]        if v == nil {            v = &CacheValue{}            c.m[key] = v        }        c.mtx.Unlock()    }    v.mtx.Lock()    if v.val == nil {        v.val = buildValue(key)    }    v.mtx.Unlock()    return v.val}

梵蒂冈之花

受到经常用于描述通道的乒乓球示例的启发,我们尝试了仅通道方法。球保存有关正在生成的密钥的状态,并且球在共享通道的请求之间传递: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)}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go