寻找一种更具Go风格的解决方案来解决以下问题:
比方说,服务器有多个并行传入的请求,请求具有密钥key的资源。由于计算此资源是昂贵的/耗时的,我们希望确保只计算一次。有无限多个可能的键。
一个简单的实现:
if hasCachedValue(key) {
return cachedValue(key)
}
if somebodyElseWorkingOn(key) {
waitUntilReady(key)
} else {
buildCacheValue(key) // time consuming
}
return cachedValue(key)到目前为止,我们已经使用共享map[string]chan bool解决了这个问题,其中第一个请求为key插入更改,随后的请求在值准备就绪时等待该更改的关闭。为了保护地图,我们使用了sync.Mutex,但我们有一种感觉,那就是有一个更好、更有活力的解决方案。
发布于 2019-12-13 02:03:02
使用singleflight包。为组声明一个包级变量:
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发布于 2019-12-13 00:40:48
下面是一段简单的代码,它可以完成您想要的操作。我测试了它,它工作正常,没有问题。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
}发布于 2019-12-13 16:28:44
受到经常用于描述通道的乒乓示例的启发,我们坐下来尝试了一种仅限通道的方法。一个ball保存有关正在生成的密钥的状态,并且ball在请求之间传递一个共享通道:
import "time"
var table = make(chan map[string]chan bool)
func keeper() {
for {
ball := <- table
table <- ball
}
}
func getResource(key string) {
// Take ball from table
ball := <- table
if wait, ok := ball[key]; ok{
println("Somebody else working on " + key + ", waiting")
table <- ball
<- wait
} else {
println("I will build " + key)
ball[key] = make(chan bool)
// Throw away ball
table <- ball
// Building value
time.Sleep(time.Millisecond * 10)
println("I built value for " + key + "!")
// Clean up ball
ball = <- table
close(ball[key])
delete(ball, key)
table <- ball
}
println("Now value for " + key + " has been built")
}
func main(){
go keeper()
ball := make(map[string]chan bool)
table <- ball
key := "key"
go getResource(key)
go getResource(key)
go getResource(key)
time.Sleep(time.Second)
}https://stackoverflow.com/questions/59307534
复制相似问题