mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-10-25 07:42:16 +00:00
fix typo
This commit is contained in:
@@ -54,7 +54,7 @@ func (memo *Memo) Get(key string) (interface{}, error) {
|
||||
}
|
||||
```
|
||||
|
||||
Memo实例会记录需要缓存的函数f(类型为Func),以及缓存内容(里面是一个string到result映射的map)。每一个result都是都是简单的函数返回的值对儿--一个值和一个错误值。继续下去我们会展示一些Memo的变种,不过所有的例子都会遵循这些上面的这些方面。
|
||||
Memo实例会记录需要缓存的函数f(类型为Func),以及缓存内容(里面是一个string到result映射的map)。每一个result都是简单的函数返回的值对儿--一个值和一个错误值。继续下去我们会展示一些Memo的变种,不过所有的例子都会遵循这些上面的这些方面。
|
||||
|
||||
下面是一个使用Memo的例子。对于流入的URL的每一个元素我们都会调用Get,并打印调用延时以及其返回的数据大小的log:
|
||||
|
||||
@@ -163,7 +163,6 @@ type Memo struct {
|
||||
|
||||
// Get is concurrency-safe.
|
||||
func (memo *Memo) Get(key string) (value interface{}, err error) {
|
||||
memo.cache[key] = res
|
||||
memo.mu.Lock()
|
||||
res, ok := memo.cache[key]
|
||||
if !ok {
|
||||
@@ -245,7 +244,7 @@ func (memo *Memo) Get(key string) (value interface{}, err error) {
|
||||
|
||||
现在Get函数包括下面这些步骤了:获取互斥锁来保护共享变量cache map,查询map中是否存在指定条目,如果没有找到那么分配空间插入一个新条目,释放互斥锁。如果存在条目的话且其值没有写入完成(也就是有其它的goroutine在调用f这个慢函数)时,goroutine必须等待值ready之后才能读到条目的结果。而想知道是否ready的话,可以直接从ready channel中读取,由于这个读取操作在channel关闭之前一直是阻塞。
|
||||
|
||||
如果没有条目的话,需要向map中插入一个没有ready的条目,当前正在调用的goroutine就需要负责调用慢函数、更新条目以及向其它所有goroutine广播条目已经ready可读的消息了。
|
||||
如果没有条目的话,需要向map中插入一个没有准备好的条目,当前正在调用的goroutine就需要负责调用慢函数、更新条目以及向其它所有goroutine广播条目已经ready可读的消息了。
|
||||
|
||||
条目中的e.res.value和e.res.err变量是在多个goroutine之间共享的。创建条目的goroutine同时也会设置条目的值,其它goroutine在收到"ready"的广播消息之后立刻会去读取条目的值。尽管会被多个goroutine同时访问,但却并不需要互斥锁。ready channel的关闭一定会发生在其它goroutine接收到广播事件之前,因此第一个goroutine对这些变量的写操作是一定发生在这些读操作之前的。不会发生数据竞争。
|
||||
|
||||
@@ -299,7 +298,7 @@ func (memo *Memo) Get(key string) (interface{}, error) {
|
||||
func (memo *Memo) Close() { close(memo.requests) }
|
||||
```
|
||||
|
||||
上面的Get方法,会创建一个response channel,把它放进request结构中,然后发送给monitor goroutine,然后马上又会接收到它。
|
||||
上面的Get方法,会创建一个response channel,把它放进request结构中,然后发送给monitor goroutine,然后马上又会接收它。
|
||||
|
||||
cache变量被限制在了monitor goroutine (\*Memo).server中,下面会看到。monitor会在循环中一直读取请求,直到request channel被Close方法关闭。每一个请求都会去查询cache,如果没有找到条目的话,那么就会创建/插入一个新的条目。
|
||||
|
||||
@@ -335,9 +334,9 @@ func (e *entry) deliver(response chan<- result) {
|
||||
|
||||
和基于互斥量的版本类似,第一个对某个key的请求需要负责去调用函数f并传入这个key,将结果存在条目里,并关闭ready channel来广播条目的ready消息。使用(\*entry).call来完成上述工作。
|
||||
|
||||
紧接着对同一个key的请求会发现map中已经有了存在的条目,然后会等待结果变为ready,并将结果从response发送给客户端的goroutien。上述工作是用(\*entry).deliver来完成的。对call和deliver方法的调用必须在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。
|
||||
紧接着对同一个key的请求会发现map中已经有了存在的条目,然后会等待结果变为ready,并将结果从response发送给客户端的goroutien。上述工作是用(\*entry).deliver来完成的。对call和deliver方法的调用必须让它们在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。
|
||||
|
||||
这个例子说明我们无论可以用上锁,还是通信来建立并发程序都是可行的。
|
||||
这个例子说明我们无论用上锁,还是通信来建立并发程序都是可行的。
|
||||
|
||||
上面的两种方案并不好说特定情境下哪种更好,不过了解他们还是有价值的。有时候从一种方式切换到另一种可以使你的代码更为简洁。(译注:不是说好的golang推崇通信并发么)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user