- 当多个 goroutine 同时调用 Do 方法时,只有第一个会真正执行函数 fn。
- 后续的调用会等待第一个调用完成,然后直接返回结果。
- 使用 sync.WaitGroup 来同步等待。
- 使用 sync.Mutex 来保护共享的 map。
type Group struct {
mu sync.Mutex // 保护 m
m map[string]*call // 懒加载
}
type call struct {
wg sync.WaitGroup
val interface{}
err error
dups int
}
- Group 结构体使用一个 map 来存储正在进行的调用。
- call 结构体表示一个正在进行的函数调用。
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error, bool) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
c.dups++
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err, true
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
g.doCall(c, key, fn)
return c.val, c.err, c.dups > 0
}
- 标准库 net/lookup.go, 如果多个请求查询同一个 host, lookGroup 会合并请求,只请求一次就行
- 缓存失效后的重新加载
- 并发请求的合并
- 避免重复计算