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