golang并发

hhxx
  • 569

先贴代码

package cache

import (
    "fmt"
)

type entry struct {
    res   result
    ready chan struct{} //closed when res is ready
}

type request struct {
    key      string
    response chan<- result
}

type Memo struct {
    requests chan request
}

type Func func(key string) (interface{}, error)

type result struct {
    value interface{}
    err   error
}

func New(f Func) *Memo {
    memo := &Memo{requests: make(chan request)}
    go memo.server(f)
    return memo
}

func (memo *Memo) Get(key string) (value interface{}, err error) {
    response := make(chan result)
    memo.requests <- request{key, response}
    res := <-response
    return res.value, res.err
}

func (memo *Memo) Close() {
    close(memo.requests)
}

func (memo *Memo) server(f Func) {
    cache := make(map[string]*entry)
    for req := range memo.requests {
        e, ok := cache[req.key]
        if !ok {
            e = &entry{ready: make(chan struct{})}
            cache[req.key] = e
            go e.call(f, req.key)
        }
        go e.deliver(req.response, req.key)
    }
}

func (e *entry) call(f Func, key string) {
    fmt.Println("request url: ", key)
    e.res.value, e.res.err = f(key)
    close(e.ready)
}

func (e *entry) deliver(response chan<- result, key string) {
    fmt.Println(key, " has been cached!")
    <-e.ready
    response <- e.res
}
// 上面是cache包

package main

import (
    "fmt"
    "github.com/zhumaohua/cache"
    "io/ioutil"
    "log"
    "net/http"
    "sync"
    "time"
)

func incomingUrls() []string {
    return []string{"https://www.segmentfault.com", "https://www.baidu.com", "http://www.sogou.com", "http://che.sogou.com", "http://m.che.sogou.com", "https://www.segmentfault.com", "https://www.baidu.com", "https://www.sogou.com", "http://che.sogou.com", "http://m.che.sogou.com", "https://www.segmentfault.com", "https://www.baidu.com", "https://www.sogou.com", "http://che.sogou.com", "http://m.che.sogou.com"}
}

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() {
    m := cache.New(httpGetBody)
    var n sync.WaitGroup
    for _, url := range incomingUrls() {
        n.Add(1)
        go func(url string) {
            defer n.Done()
            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)))
        }(url)
    }
    n.Wait()
}

代码是简单的一个可以并发读取的缓存,但是输出如下
图片描述

有几个地方不明白,首先为什么每次get都命中了缓存,但是其实压根没有缓存啊,不能理解为什么输出了那么多次缓存命中,而且证明缓存根本没发挥作用,因为根本没有降低时间,理论上后面的会是ns级别返回才对,请指教下为什么会这样呢?看着代码没什么问题啊,基本上是照着gopl上面敲的啊

回复
阅读 3k
1 个回答
  1. 为什么每次都命中缓存。你看xxx has been cached这句话是在deliver里面的,deliver无论是缓存还是不缓存都会调用!所以这句话打出来不能说明是不是缓存了。你应该反过来,输出没有缓存的日志,这样应该能理解了:

if !ok {
    fmt.Println(key, " has not been cached!")
    e = &entry{ready: make(chan struct{})}
    cache[req.key] = e
    go e.call(f, req.key)
}
  1. 缓存没发挥作用。我看了下代码,似乎代码没什么问题,你按照我上面的代码加一句has not been cached!试试看

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏