ch9-7: fmt code

This commit is contained in:
chai2010 2016-01-18 17:51:17 +08:00
parent 687549b05d
commit 86650d2d26

View File

@ -6,12 +6,12 @@
```go ```go
func httpGetBody(url string) (interface{}, error) { func httpGetBody(url string) (interface{}, error) {
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
return ioutil.ReadAll(resp.Body) return ioutil.ReadAll(resp.Body)
} }
``` ```
@ -27,30 +27,30 @@ package memo
// A Memo caches the results of calling a Func. // A Memo caches the results of calling a Func.
type Memo struct { type Memo struct {
f Func f Func
cache map[string]result cache map[string]result
} }
// Func is the type of the function to memoize. // Func is the type of the function to memoize.
type Func func(key string) (interface{}, error) type Func func(key string) (interface{}, error)
type result struct { type result struct {
value interface{} value interface{}
err error err error
} }
func New(f Func) *Memo { func New(f Func) *Memo {
return &Memo{f: f, cache: make(map[string]result)} return &Memo{f: f, cache: make(map[string]result)}
} }
// NOTE: not concurrency-safe! // NOTE: not concurrency-safe!
func (memo *Memo) Get(key string) (interface{}, error) { func (memo *Memo) Get(key string) (interface{}, error) {
res, ok := memo.cache[key] res, ok := memo.cache[key]
if !ok { if !ok {
res.value, res.err = memo.f(key) res.value, res.err = memo.f(key)
memo.cache[key] = res memo.cache[key] = res
} }
return res.value, res.err return res.value, res.err
} }
``` ```
@ -61,13 +61,13 @@ Memo實例會記録需要緩存的函數f(類型爲Func),以及緩存內容(
```go ```go
m := memo.New(httpGetBody) m := memo.New(httpGetBody)
for url := range incomingURLs() { for url := range incomingURLs() {
start := time.Now() start := time.Now()
value, err := m.Get(url) value, err := m.Get(url)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
} }
fmt.Printf("%s, %s, %d bytes\n", fmt.Printf("%s, %s, %d bytes\n",
url, time.Since(start), len(value.([]byte))) url, time.Since(start), len(value.([]byte)))
} }
``` ```
@ -97,17 +97,17 @@ ok gopl.io/ch9/memo1 1.257s
m := memo.New(httpGetBody) m := memo.New(httpGetBody)
var n sync.WaitGroup var n sync.WaitGroup
for url := range incomingURLs() { for url := range incomingURLs() {
n.Add(1) n.Add(1)
go func(url string) { go func(url string) {
start := time.Now() start := time.Now()
value, err := m.Get(url) value, err := m.Get(url)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
} }
fmt.Printf("%s, %s, %d bytes\n", fmt.Printf("%s, %s, %d bytes\n",
url, time.Since(start), len(value.([]byte))) url, time.Since(start), len(value.([]byte)))
n.Done() n.Done()
}(url) }(url)
} }
n.Wait() n.Wait()
``` ```
@ -156,24 +156,24 @@ memo.go的32行出現了兩次説明有兩個goroutine在沒有同步榦預
```go ```go
gopl.io/ch9/memo2 gopl.io/ch9/memo2
type Memo struct { type Memo struct {
f Func f Func
mu sync.Mutex // guards cache mu sync.Mutex // guards cache
cache map[string]result cache map[string]result
} }
// Get is concurrency-safe. // Get is concurrency-safe.
func (memo *Memo) Get(key string) (value interface{}, err error) { func (memo *Memo) Get(key string) (value interface{}, err error) {
res, ok := memo.cache[key] if!ok{ res, ok := memo.cache[key] if!ok{
res.value, res.err = memo.f(key) res.value, res.err = memo.f(key)
memo.cache[key] = res memo.cache[key] = res
memo.mu.Lock() memo.mu.Lock()
res, ok := memo.cache[key] res, ok := memo.cache[key]
if !ok { if !ok {
res.value, res.err = memo.f(key) res.value, res.err = memo.f(key)
memo.cache[key] = res memo.cache[key] = res
} }
memo.mu.Unlock() memo.mu.Unlock()
return res.value, res.err return res.value, res.err
} }
``` ```
@ -184,19 +184,19 @@ func (memo *Memo) Get(key string) (value interface{}, err error) {
```go ```go
gopl.io/ch9/memo3 gopl.io/ch9/memo3
func (memo *Memo) Get(key string) (value interface{}, err error) { func (memo *Memo) Get(key string) (value interface{}, err error) {
memo.mu.Lock() memo.mu.Lock()
res, ok := memo.cache[key] res, ok := memo.cache[key]
memo.mu.Unlock() memo.mu.Unlock()
if !ok { if !ok {
res.value, res.err = memo.f(key) res.value, res.err = memo.f(key)
// Between the two critical sections, several goroutines // Between the two critical sections, several goroutines
// may race to compute f(key) and update the map. // may race to compute f(key) and update the map.
memo.mu.Lock() memo.mu.Lock()
memo.cache[key] = res memo.cache[key] = res
memo.mu.Unlock() memo.mu.Unlock()
} }
return res.value, res.err return res.value, res.err
} }
``` ```
@ -207,41 +207,41 @@ func (memo *Memo) Get(key string) (value interface{}, err error) {
```go ```go
gopl.io/ch9/memo4 gopl.io/ch9/memo4
type entry struct { type entry struct {
res result res result
ready chan struct{} // closed when res is ready ready chan struct{} // closed when res is ready
} }
func New(f Func) *Memo { func New(f Func) *Memo {
return &Memo{f: f, cache: make(map[string]*entry)} return &Memo{f: f, cache: make(map[string]*entry)}
} }
type Memo struct { type Memo struct {
f Func f Func
mu sync.Mutex // guards cache mu sync.Mutex // guards cache
cache map[string]*entry cache map[string]*entry
} }
func (memo *Memo) Get(key string) (value interface{}, err error) { func (memo *Memo) Get(key string) (value interface{}, err error) {
memo.mu.Lock() memo.mu.Lock()
e := memo.cache[key] e := memo.cache[key]
if e == nil { if e == nil {
// This is the first request for this key. // This is the first request for this key.
// This goroutine becomes responsible for computing // This goroutine becomes responsible for computing
// the value and broadcasting the ready condition. // the value and broadcasting the ready condition.
e = &entry{ready: make(chan struct{})} e = &entry{ready: make(chan struct{})}
memo.cache[key] = e memo.cache[key] = e
memo.mu.Unlock() memo.mu.Unlock()
e.res.value, e.res.err = memo.f(key) e.res.value, e.res.err = memo.f(key)
close(e.ready) // broadcast ready condition close(e.ready) // broadcast ready condition
} else { } else {
// This is a repeat request for this key. // This is a repeat request for this key.
memo.mu.Unlock() memo.mu.Unlock()
<-e.ready // wait for ready condition <-e.ready // wait for ready condition
} }
return e.res.value, e.res.err return e.res.value, e.res.err
} }
``` ```
@ -263,13 +263,13 @@ type Func func(key string) (interface{}, error)
// A result is the result of calling a Func. // A result is the result of calling a Func.
type result struct { type result struct {
value interface{} value interface{}
err error err error
} }
type entry struct { type entry struct {
res result res result
ready chan struct{} // closed when res is ready ready chan struct{} // closed when res is ready
} }
``` ```
@ -279,23 +279,23 @@ type entry struct {
gopl.io/ch9/memo5 gopl.io/ch9/memo5
// A request is a message requesting that the Func be applied to key. // A request is a message requesting that the Func be applied to key.
type request struct { type request struct {
key string key string
response chan<- result // the client wants a single result response chan<- result // the client wants a single result
} }
type Memo struct{ requests chan request } type Memo struct{ requests chan request }
// New returns a memoization of f. Clients must subsequently call Close. // New returns a memoization of f. Clients must subsequently call Close.
func New(f Func) *Memo { func New(f Func) *Memo {
memo := &Memo{requests: make(chan request)} memo := &Memo{requests: make(chan request)}
go memo.server(f) go memo.server(f)
return memo return memo
} }
func (memo *Memo) Get(key string) (interface{}, error) { func (memo *Memo) Get(key string) (interface{}, error) {
response := make(chan result) response := make(chan result)
memo.requests <- request{key, response} memo.requests <- request{key, response}
res := <-response res := <-response
return res.value, res.err return res.value, res.err
} }
func (memo *Memo) Close() { close(memo.requests) } func (memo *Memo) Close() { close(memo.requests) }
@ -307,31 +307,31 @@ cache變量被限製在了monitor goroutine (\*Memo).server中下面會看到
```go ```go
func (memo *Memo) server(f Func) { func (memo *Memo) server(f Func) {
cache := make(map[string]*entry) cache := make(map[string]*entry)
for req := range memo.requests { for req := range memo.requests {
e := cache[req.key] e := cache[req.key]
if e == nil { if e == nil {
// This is the first request for this key. // This is the first request for this key.
e = &entry{ready: make(chan struct{})} e = &entry{ready: make(chan struct{})}
cache[req.key] = e cache[req.key] = e
go e.call(f, req.key) // call f(key) go e.call(f, req.key) // call f(key)
} }
go e.deliver(req.response) go e.deliver(req.response)
} }
} }
func (e *entry) call(f Func, key string) { func (e *entry) call(f Func, key string) {
// Evaluate the function. // Evaluate the function.
e.res.value, e.res.err = f(key) e.res.value, e.res.err = f(key)
// Broadcast the ready condition. // Broadcast the ready condition.
close(e.ready) close(e.ready)
} }
func (e *entry) deliver(response chan<- result) { func (e *entry) deliver(response chan<- result) {
// Wait for the ready condition. // Wait for the ready condition.
<-e.ready <-e.ready
// Send the result to the client. // Send the result to the client.
response <- e.res response <- e.res
} }
``` ```
@ -343,5 +343,5 @@ func (e *entry) deliver(response chan<- result) {
上面的兩種方案併不好説特定情境下哪種更好,不過了解他們還是有價值的。有時候從一種方式切換到另一種可以使你的代碼更爲簡潔。(譯註不是説好的golang推崇通信併發麽) 上面的兩種方案併不好説特定情境下哪種更好,不過了解他們還是有價值的。有時候從一種方式切換到另一種可以使你的代碼更爲簡潔。(譯註不是説好的golang推崇通信併發麽)
練習 9.3: 擴展Func類型和(\*Memo).Get方法支持調用方提供一個可選的done channel使其具備通過該channel來取消整個操作的能力(§8.9)。一個被取消了的Func的調用結果不應該被緩存。 **練習 9.3** 擴展Func類型和(\*Memo).Get方法支持調用方提供一個可選的done channel使其具備通過該channel來取消整個操作的能力(§8.9)。一個被取消了的Func的調用結果不應該被緩存。