🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[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 ```