[日常] Go语言圣经-并发的非阻塞缓存
发布日期:2021-05-18 07:59:29 浏览次数:11 分类:精选文章

本文共 2522 字,大约阅读时间需要 8 分钟。

1. Go测试工具是基于约定和规范进行测试的命令。

2. 使用`go run -race`命令可以检测共享变量访问中的数据竞争情况。该工具会显示WARNING: DATA RACE。

3. 理想情况下,应该避免不必要的工作,这被称为Duplicate Suppression

4. 构建并发、无重复、无阻塞的缓存:

通过以下方式实现并发:

1. 使用`go func() {}()`启动新的goroutine。

2.确保并发安全:使用sync.Mutex互斥锁。

3.优化HTTP请求bole:

在获取之前对缓存位置加锁定,并为入口指针分配存όρ后立即解锁。这样可以避免被慢的HTTP请求阻塞。

4. 去重存储:

使用渠道机制,批量处理请求。在第一个请求完成后关闭渠道,这样其他goroutine可以直接调用。

package main

import ("fmt""golang.org/x/net/html""io/ioutil""log""net/http""sync""time")

type Memo struct {Func Funcmu sync.Mutexcache map[string]*entry}

type result struct {value interface{}err error}

type entry struct {res resultready chan struct{} // Closed when res is ready}

func main() {// Initialize Memom := New(httpGetBody)urls, _ := Extract("http://www.baidu.com")

var n sync.WaitGroupfor _, url := range urls {    n.Add(1)    go func(url string) {        fmt.Println(url)        start := time.Now()        value, err := m.Get(url)        if err != nil {            log.Print(err)        }        if value != nil {            fmt.Printf("%s, %s, %d bytes\n", url, time.Since(start), len(value.([]byte)))        }        n.Done()    }(url)}n.Wait()

}

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()if e := memo.cache[key]; e == nil {e = &entry{ready: make(chan struct{})}memo.cache[key] = ememo.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}

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 Extract(url string) ([]string, error) {resp, err := http.Get(url)if err != nil {return nil, err}if resp.StatusCode != http.StatusOK {resp.Body.Close()return nil, fmt.Errorf("getting %s: %s", url, resp.Status)}doc, err := html.Parse(resp.Body)resp.Body.Close()if err != nil {return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)}var links []stringvisitNode := func(n *html.Node) {if n.Type == html.ElementNode && n.Data == "a" {for _, a := range n.Attr {if a.Key != "href" {continue}link, err := resp.Request.URL.Parse(a.Val)if err != nil {continue // Ignore bad URLs}links = append(links, link.String())}}}forEachNode(doc, visitNode, nil)return links, nil}

func forEachNode(n *html.Node, pre, post func(n *html.Node)) {if pre != nil {pre(n)}for c := n.FirstChild; c != nil; c = c.NextSibling {forEachNode(c, pre, post)}if post != nil {post(n)}}

上一篇:[日常] Go语言圣经-Goroutines和线程
下一篇:[日常] Go语言圣经-竞争条件习题

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2025年04月27日 16时56分09秒