参考https://book.itsfun.top/gopl-zh/ch9/ch9-07.html
package mainimport ("fmt""io/ioutil""log""net/http""sync""time")type Memo struct {f Funcrequests chan request}type Func func(key string) (interface{}, error)// 一个结果的值type result struct {value interface{}err error}// 一个cache单元type entry struct {res resultready chan struct{} // closed when res is ready}// 一个请求type request struct {key stringres chan result // 用来接收结果}// 缓存未命中时,调用费时的F函数func (e *entry) call (f Func, key string) {var res resultres.value, res.err = f(key)e.res = resclose(e.ready) // 用来表明res中的值已就绪}// 将结果返回给请求者func (e *entry) deliver(resChan chan <- result ) {<- e.ready // 当e.ready被close后,这里不会再阻塞resChan <- e.res}func New(f Func) *Memo {memo := &Memo{f: f, requests: make(chan request)}go memo.serve()return memo}// 一直接收request请求,并依据cache进行返回func (memo *Memo) serve() {cache := make(map[string]*entry)for req := range memo.requests {e := cache[req.key]if e == nil { // cache未命中e = &entry{ready: make(chan struct{})}cache[req.key] = ego e.call(memo.f, req.key)}go e.deliver(req.res) // 这里必须用另一个线程去做,保证for循环的流动}}// Get 读cachefunc (memo *Memo) Get(key string) (interface{}, error) {resc := make(chan result)memo.requests <- request{key: key, res: resc}res := <- rescreturn res.value, res.err}// 费时的函数func httpGetBody(url string) (interface{}, error) {resp, err := http.Get(url)if err != nil {return nil, err}defer resp.Body.Close()return ioutil.ReadAll(resp.Body)}func main() {urls := []string{"http://www.baidu.com","http://www.163.com","http://www.qq.com","http://www.baidu.com","http://www.baidu.com","http://www.163.com","http://www.qq.com","http://stackoverflow.com","http://stackoverflow.com",}m := New(httpGetBody)var n sync.WaitGroupfor _, url := range urls {n.Add(1)go func(url string) {start := time.Now()value, err := m.Get(url)if err != nil {log.Print(err)}fmt.Printf("%s, %s, %d bytes\n",url, time.Since(start), len(value.([]byte)))n.Done()}(url)}n.Wait()}
