并发非阻塞缓存
最后更新于:2022-04-02 02:36:42
[TOC]
## 并发非阻塞缓存
通过函数传入 key 获取去获取信息,如果存在多个key,则只会进行执行一次函数,
```
type result struct {
value interface{}
err error
}
type entry struct {
res result
ready chan struct{} // closed when res is ready
}
type Mem struct {
mu sync.Mutex
cache map[string]*entry
f Func
}
type Func func(key string) (interface{}, error)
func NewMem(f Func) *Mem {
return &Mem{cache: map[string]*entry{}, f: f}
}
func (m *Mem) Get(key string) (interface{}, error) {
m.mu.Lock()
e := m.cache[key]
if e == nil {
e = &entry{ready: make(chan struct{})}
m.cache[key] = e
m.mu.Unlock()
e.res.value, e.res.err = m.f(key)
close(e.ready)
} else {
m.mu.Unlock()
<-e.ready
}
return e.res.value, e.res.err
}
```
goroutine必须等待值ready之后才能读到条目的结果。这个读取操作在channel关闭之前一直是阻塞
测试
```var url = []string{
"http://www.baidu.com",
"http://www.bilibili.com",
"http://www.baidu.com",
"http://www.baidu.com",
"http://www.baidu.com",
}
httpGetBody := func(url string) (interface{}, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
bytes, err := ioutil.ReadAll(resp.Body)
return bytes, err
}
mem := NewMem(httpGetBody)
for _, v := range url {
go func(v string) {
fmt.Printf("%+v\n", v)
b, e := mem.Get(v)
if e != nil {
log.Printf("%v", e)
}
fmt.Printf("%+v\n", len(b.([]byte)))
}(v)
}
s := make(chan os.Signal)
signal.Notify(s, os.Kill, os.Interrupt)
<-s
//output
//http://www.baidu.com
//http://www.baidu.com
//http://www.baidu.com
//http://www.bilibili.com
//http://www.baidu.com
//3530
//294264
//294264
//294264
//294264
```
';