3 回答
TA贡献1811条经验 获得超6个赞
golang.org/x/sync/singleflight包正是为此目的而编写的。
请注意,所有缓存访问都应该发生在传递给 Do 的回调函数中。在您在评论中链接到的代码中,您在外部进行查找;这有点违背了目的。
此外,您必须使用指向 singleflight.Group 的指针。这就是你的数据竞赛的来源,请去 vet 指出:
./foo.go:41:10:fetchPost 按值传递锁:命令行参数。PostManager 包含 golang.org/x/sync/singleflight.Group 包含 sync.Mutex
这是我的写法(操场上的完整示例:https: //play.golang.org/p/2hE721uA88S):
import (
"strconv"
"sync"
"golang.org/x/sync/singleflight"
)
type PostManager struct {
sf *singleflight.Group
cache *sync.Map
}
func (pc *PostManager) Fetch(id int) Post {
x, _, _ := pc.sf.Do(strconv.Itoa(id), func() (interface{}, error) {
post, ok := pc.cache.Load(id)
if !ok {
post = pc.fetchPost(id)
pc.cache.Store(id, post)
}
return post, nil
})
return x.(Post)
}
TA贡献1830条经验 获得超3个赞
如果提取已经在进行中,看起来可以使用第二张地图等待。
type PostManager struct {
sync.Map
q sync.Map
}
func (pc *PostManager) Fetch(id int) Post {
post, ok := pc.Load(id)
if ok {
fmt.Printf("Using cached post %v\n", id)
return post.(Post)
}
fmt.Printf("Fetching post %v\n", id)
if c, loaded := pc.q.LoadOrStore(id, make(chan struct{})); !loaded {
post = pc.fetchPost(id)
pc.Store(id, post)
close(c.(chan struct{}))
} else {
<-c.(chan struct{})
post,_ = pc.Load(id)
}
return post.(Post)
}
或者,更复杂一点,使用相同的地图;-)
func (pc *PostManager) Fetch(id int) Post {
p, ok := pc.Load(id)
if !ok {
fmt.Printf("Fetching post %v\n", id)
if p, ok = pc.LoadOrStore(id, make(chan struct{})); !ok {
fetched = pc.fetchPost(id)
pc.Store(id, fetched)
close(p.(chan struct{}))
return fetched
}
}
if cached, ok := p.(Post); ok {
fmt.Printf("Using cached post %v\n", id)
return cached
}
fmt.Printf("Wating for cached post %v\n", id)
<-p.(chan struct{})
return pc.Fetch(id)
}
TA贡献1827条经验 获得超9个赞
您可以使用两张地图来做到这一点,一张保留缓存的值,另一张保留正在获取的值。您还需要将锁保持更长时间,这样就不需要保持同步地图,常规地图就可以了。像这样的东西应该可以工作(未经测试):
type PostManager struct {
sync.Mutex
cached map[int]Post
loading map[int]chan struct{}
}
您需要处理以下加载失败的情况:
// Need to pass pointer pc
func (pc *PostManager) Fetch(id int) Post {
pc.Lock()
post, ok:=pc.cached[id]
if ok {
pc.Unlock()
return post
}
// See if it is being loaded
loading, ok:=pc.loading[id]
if ok {
// Wait for the loading to complete
pc.Unlock()
<-loading
// Reload
pc.Lock()
post,ok:=pc.cached[id]
// Maybe you need to handle the case where loading failed?
pc.Unlock()
return post
}
// load it
loading=make(chan struct{})
pc.loading[id]=loading
pc.Unlock()
post = pc.fetchPost(id)
pc.Lock()
pc.cached[id]=post
delete(pc.loading,id)
pc.Unlock()
close(loading)
return post
}
- 3 回答
- 0 关注
- 106 浏览
添加回答
举报