package main import ( "fmt" "io/ioutil" "net/http" "sync" "time" ) type entry struct { res result ready chan struct{} // 重复抑制 } type result struct { value interface{} err error } type Memo struct { f Func mu sync.Mutex cache map[string]*entry // 本地缓存 } type Func func(key string) (interface{}, error) func New(f Func) *Memo { return &Memo{f: f, cache: make(map[string]*entry)} } func (memo *Memo) Get(key string) (interface{}, error) { memo.mu.Lock() e := memo.cache[key] if e == nil { e = &entry{ready: make(chan struct{})} memo.cache[key] = e memo.mu.Unlock() e.res.value, e.res.err = memo.f(key) close(e.ready) } else { fmt.Println(key) memo.mu.Unlock() <-e.ready } return e.res.value, e.res.err } func httpGetBody(url string) (interface{}, error) { client := http.Client{Timeout: 1 * time.Second} resp, err := client.Get(url) if err != nil { return nil, err } defer resp.Body.Close() return ioutil.ReadAll(resp.Body) } func incomingURLs() []string { return []string{ "https://blog.csdn.net/liyuxing6639801", "https://zhuanlan.zhihu.com/p/95056679", "https://blog.csdn.net/chenbaoke/article/details/42782767", "https://www.jianshu.com/p/228c119a7d0e", "https://github.com/disintegration/imaging", "https://github.com/disintegration/imaging", } } func main() { m := New(httpGetBody) n := sync.WaitGroup{} for _, url := range incomingURLs() { n.Add(1) go func(url string) { start := time.Now() value, err := m.Get(url) if err != nil { fmt.Println(err) n.Done() return } fmt.Printf("%s, %s, %d bytes\n", url, time.Since(start), len(value.([]byte))) n.Done() }(url) } n.Wait() }