golang练手小项目系列(5)-并发无阻塞缓存

李说的对 · · 511 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

问题描述:

如下的函数被并发调用时,如果有重复的URL(在一个routine中被多次访问或多个routine访问同一个url),会产生冗余的网络请求。请实现一个并发安全的缓存,以提高网络的利用效率和降低函数的执行时间。完成后你将熟悉互斥锁的使用。

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)

}


要点:

注意多个routine同时访问同一个URL时只需要发出一个网络请求。


拓展:

代码实现1:

import (

  "sync"

)

type result struct{

  value interface{}

  err error

}

type entry struct {

  res result

  ready chan struct{}

}

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

func New(f Func) *Memo{

  return &Memo{f: f, cache: make(map[string] *entry)}

}

type Memo struct{

  f Func

  mu sync.Mutex

  cache map[string] *entry

}

func (memo *Memo) Get(key string)(value interface{}, err 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 {

      memo.mu.Unlock()

      <-e.ready

  }

  return e.res.value, e.res.err

}

代码实现2:

type result struct{

  value interface{}

  err error

}

type entry struct {

  res result

  ready chan struct{}

}

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

type request struct{

  key string

  response chan <- result

}

func New(f Func) *Memo{

  memo := &Memo{requests: make(chan request)}

  go memo.server(f)

  return memo

}

type Memo struct{

  requests chan request

}

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 := cache[req.key]

      if e == nil{

        e = &entry{ready: make(chan struct{})}

        cache[req.key] = e

        go e.call(f, req.key)

      }

      go e.deliver(req.response)

  }

}

func (e *entry) call(f Func, key string){

  e.res.value, e.res.err = f(key)

  close(e.ready)

}

func (e *entry) deliver(response chan <- result)  {

  <- e.ready

  response <- e.res

}


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:李说的对

查看原文:golang练手小项目系列(5)-并发无阻塞缓存

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

511 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传