Caching 通道并发保证

Caching 通道并发保证,caching,go,concurrency,channel,memoization,Caching,Go,Concurrency,Channel,Memoization,我正在写一份并发安全备忘录: package mu import ( "sync" ) // Func represents a memoizable function, operating on a string key, to use with a Mu type Func func(key string) interface{} // Mu is a cache that memoizes results of an expensive computation // // I

我正在写一份并发安全备忘录:

package mu

import (
    "sync"
)

// Func represents a memoizable function, operating on a string key, to use with a Mu
type Func func(key string) interface{}

// Mu is a cache that memoizes results of an expensive computation
//
// It has a traditional implementation using mutexes.
type Mu struct {
    // guards done
    mu   sync.RWMutex
    done map[string]chan bool
    memo map[string]interface{}
    f    Func
}

// Get a string key if it exists, otherwise computes the value and caches it.
//
// Returns the value and whether or not the key existed.
func (c *Mu) Get(key string) (interface{}, bool) {
    c.mu.RLock()
    _, ok := c.done[key]
    c.mu.RUnlock()
    if ok {
        return c.get(key), true
    }

    c.mu.Lock()
    _, ok = c.done[key]
    if ok {
        c.mu.Unlock()
    } else {
        c.done[key] = make(chan bool)
        c.mu.Unlock()

        v := c.f(key)
        c.memo[key] = v

        close(c.done[key])
    }
    return c.get(key), ok
}

// get returns the value of key, blocking on an existing computation
func (c *Mu) get(key string) interface{} {
    <-c.done[key]
    v, _ := c.memo[key]
    return v
}
包mu
进口(
“同步”
)
//Func表示一个可记忆的函数,在字符串键上操作,与Mu一起使用
类型Func Func(键字符串)接口{}
//Mu是一种缓存,用于存储昂贵计算的结果
//
//它有一个使用互斥锁的传统实现。
类型Mu结构{
//警卫完毕
mu sync.RWMutex
完成映射[字符串]chan bool
备忘录映射[字符串]接口{}
f函数
}
//获取字符串键(如果存在),否则计算值并缓存它。
//
//返回值以及键是否存在。
func(c*Mu)Get(键字符串)(接口{},bool){
c、 mu.RLock()
_,确定:=c.done[key]
c、 mu.RUnlock()
如果可以的话{
返回c.get(键),true
}
c、 木锁()
_,ok=c.完成[键]
如果可以的话{
c、 mu.Unlock()
}否则{
c、 完成[关键点]=制造(chan bool)
c、 mu.Unlock()
v:=c.f(键)
c、 备注[键]=v
关闭(c.完成[键])
}
返回c.get(键),好吗
}
//get返回key的值,阻塞现有计算
func(c*Mu)get(键字符串)接口{}{

简而言之,答案是肯定的

我们可以简化一些代码以获得为什么的精华。请考虑您的<代码> MU/COD>结构:

type Mu struct {
    memo int
    done chan bool
}
我们现在可以定义两个函数,
compute
read

func compute(r *Mu) {
    time.Sleep(2 * time.Second)
    r.memo = 42
    close(r.done)
}

func read(r *Mu) {
    <-r.done
    fmt.Println("Read value: ", r.memo)
}
在这三种情况下,我们都打印出计算任务的结果

工作代码

当您在go中“关闭”一个通道时,所有等待通道结果的语句(包括关闭后执行的语句)都将被阻塞。因此,如果通道关闭的唯一位置是计算备注值的位置,您将获得该保证

唯一需要注意的地方是确保此通道在代码中的任何其他地方都没有关闭

func main() {
    r := &Mu{}
    r.done = make(chan bool)
    go compute(r)

    // this one starts immediately
    go read(r)
    time.Sleep(time.Second)

    // this one starts in the middle of computation
    go read(r)
    time.Sleep(2*time.Second)

    // this one starts after the computation is complete
    go read(r)

    // This is to prevent the program from terminating immediately
    time.Sleep(3 * time.Second)
}