mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-11-28 09:09:07 +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
|
```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的調用結果不應該被緩存。
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user