【发布时间】:2020-02-12 23:50:59
【问题描述】:
In answering another question 我写了一个小结构体,使用sync.Map 来缓存 API 请求。
type PostManager struct {
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)
post = pc.fetchPost(id)
pc.Store(id, post)
return post.(Post)
}
不幸的是,如果两个 goroutine 同时获取同一个未缓存的 Post,它们都会发出请求。
var postManager PostManager
wg.Add(3)
var firstPost Post
var secondPost Post
var secondPostAgain Post
go func() {
// Fetches and caches 1
firstPost = postManager.Fetch(1)
defer wg.Done()
}()
go func() {
// Fetches and caches 2
secondPost = postManager.Fetch(2)
defer wg.Done()
}()
go func() {
// Also fetches and caches 2
secondPostAgain = postManager.Fetch(2)
defer wg.Done()
}()
wg.Wait()
我需要确保当同时提取相同 ID 时,只允许一个实际发出请求。另一个必须等待并将使用缓存的 Post。但也不要锁定不同 ID 的提取。
在上面的示例中,我希望对pc.fetchPost(1) 和pc.fetchPost(2) 进行一次且只有一次调用,并且它们应该是同时进行的。
【问题讨论】:
-
"包 singleflight 提供了重复的函数调用抑制机制。" godoc.org/golang.org/x/sync/singleflight
-
@Peter 这很棒,也是迄今为止最简单的解决方案。如果您发布答案,我会接受。但是,
go run --race在 Do() 处显示了数据竞争。 Code here.