Goroutines and channels are bothering me these days. I'm looking at the memo5 code at https://github.com/adonovan/gopl.io/tree/master/ch9.
If you look at memo.go of memo5, there are func (e *entry) call(f Func, key string)
and func (e *entry) deliver(response chan<- result)
parts.
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 278.
// Package memo provides a concurrency-safe non-blocking memoization
// of a function. Requests for different keys proceed in parallel.
// Concurrent requests for the same key block until the first completes.
// This implementation uses a monitor goroutine.
package memo
//! Func
// Func is the type of the function to memoize.
type Func func(key string) (interface{}, error)
// A result is the result of calling a Func.
type result struct {
value interface{}
err error
}
type entry struct {
res result
ready chan struct{} // closed when res is ready
}
//!-Func
//! get
// 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
}
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
}
func (memo *Memo) Get(key string) (interface{}, error) {
response := make(chan result)
memo.requests <- request{key, response}
res := <-response
return res.value, res.err
}
func (memo *Memo) Close() { close(memo.requests) }
//!-get
//! monitor
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)
}
}
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)
}
func (e *entry) deliver(response chan<- result) {
// Wait for the ready condition.
<-e.ready
// Send the result to the client.
response <- e.res
}
//!-monitor
I don't understand how close(e.ready)
and <-e.ready
synchronize here. Even after reading the book, I don't understand. Please tell me what the mechanism is.
CodePudding user response:
e.ready
is more known as done
channel. this is a way to tell your ref functions that domething is ready by closing done
(e.ready
chan). <-e.ready
is going to block until its done
(closed
).
so... reading this code it means next.
deliver
waits for rediness signal.call
gets data for e.res(
e.res.value, e.res.err = f(key)`)call
releas done channel by closing it (close(e.ready)
)deliver
can move throu block reading from<-e.ready
and send data toresponse