参考https://book.itsfun.top/gopl-zh/ch9/ch9-07.html

    1. package main
    2. import (
    3. "fmt"
    4. "io/ioutil"
    5. "log"
    6. "net/http"
    7. "sync"
    8. "time"
    9. )
    10. type Memo struct {
    11. f Func
    12. requests chan request
    13. }
    14. type Func func(key string) (interface{}, error)
    15. // 一个结果的值
    16. type result struct {
    17. value interface{}
    18. err error
    19. }
    20. // 一个cache单元
    21. type entry struct {
    22. res result
    23. ready chan struct{} // closed when res is ready
    24. }
    25. // 一个请求
    26. type request struct {
    27. key string
    28. res chan result // 用来接收结果
    29. }
    30. // 缓存未命中时,调用费时的F函数
    31. func (e *entry) call (f Func, key string) {
    32. var res result
    33. res.value, res.err = f(key)
    34. e.res = res
    35. close(e.ready) // 用来表明res中的值已就绪
    36. }
    37. // 将结果返回给请求者
    38. func (e *entry) deliver(resChan chan <- result ) {
    39. <- e.ready // 当e.ready被close后,这里不会再阻塞
    40. resChan <- e.res
    41. }
    42. func New(f Func) *Memo {
    43. memo := &Memo{f: f, requests: make(chan request)}
    44. go memo.serve()
    45. return memo
    46. }
    47. // 一直接收request请求,并依据cache进行返回
    48. func (memo *Memo) serve() {
    49. cache := make(map[string]*entry)
    50. for req := range memo.requests {
    51. e := cache[req.key]
    52. if e == nil { // cache未命中
    53. e = &entry{ready: make(chan struct{})}
    54. cache[req.key] = e
    55. go e.call(memo.f, req.key)
    56. }
    57. go e.deliver(req.res) // 这里必须用另一个线程去做,保证for循环的流动
    58. }
    59. }
    60. // Get 读cache
    61. func (memo *Memo) Get(key string) (interface{}, error) {
    62. resc := make(chan result)
    63. memo.requests <- request{key: key, res: resc}
    64. res := <- resc
    65. return res.value, res.err
    66. }
    67. // 费时的函数
    68. func httpGetBody(url string) (interface{}, error) {
    69. resp, err := http.Get(url)
    70. if err != nil {
    71. return nil, err
    72. }
    73. defer resp.Body.Close()
    74. return ioutil.ReadAll(resp.Body)
    75. }
    76. func main() {
    77. urls := []string{
    78. "http://www.baidu.com",
    79. "http://www.163.com",
    80. "http://www.qq.com",
    81. "http://www.baidu.com",
    82. "http://www.baidu.com",
    83. "http://www.163.com",
    84. "http://www.qq.com",
    85. "http://stackoverflow.com",
    86. "http://stackoverflow.com",
    87. }
    88. m := New(httpGetBody)
    89. var n sync.WaitGroup
    90. for _, url := range urls {
    91. n.Add(1)
    92. go func(url string) {
    93. start := time.Now()
    94. value, err := m.Get(url)
    95. if err != nil {
    96. log.Print(err)
    97. }
    98. fmt.Printf("%s, %s, %d bytes\n",
    99. url, time.Since(start), len(value.([]byte)))
    100. n.Done()
    101. }(url)
    102. }
    103. n.Wait()
    104. }