为了账号安全,请及时绑定邮箱和手机立即绑定

Go 中对单个计算成本高的资源的多个请求

Go 中对单个计算成本高的资源的多个请求

Go
白猪掌柜的 2022-04-26 10:36:43
为以下问题寻找更 Go-ish 的解决方案:比如说,一个服务器有多个并行传入的请求,请求一个带有 key 的资源key。由于计算这个资源是昂贵的/耗时的,我们希望确保它只计算一次。有无数个可能的键。一个天真的实现:if hasCachedValue(key) {   return cachedValue(key)}if somebodyElseWorkingOn(key) {   waitUntilReady(key)} else {   buildCacheValue(key) // time consuming}return cachedValue(key)到目前为止,我们已经使用 shared 解决了这个问题map[string]chan bool,其中第一个请求插入 chan key,当值准备好时,以下请求等待该 chan 的关闭。为了保护地图,我们使用了sync.Mutex,但我们感觉有一个更好、更 Go-ish 的解决方案。
查看完整描述

3 回答

?
ITMISS

TA贡献1871条经验 获得超8个赞

使用单程包。为组声明一个包级变量:


var g singleflight.Group

使用以下代码获取值:


v, err, _ := g.Do(key, func() (interface{}, error) {

    if !hasCachedValue(key) {

        buildCacheValue(key)

    }

    return cachedValue(key), nil

})

if err != nil {

    // handle error

}

x := v.(valueType) // assert to type returned by cachedValue

// do something with x


查看完整回答
反对 回复 2022-04-26
?
慕田峪9158850

TA贡献1794条经验 获得超7个赞

受到经常用于描述频道的 Ping Pong 示例的启发,我们坐下来尝试仅使用频道的方法。一个球保持关于正在生成的密钥的状态,并且球在请求之间传递一个共享通道:


import "time"


var table = make(chan map[string]chan bool)


func keeper() {

    for {

        ball := <- table

        table <- ball

    }

}


func getResource(key string) {

    // Take ball from table

    ball := <- table


    if wait, ok := ball[key]; ok{

        println("Somebody else working on " + key + ", waiting")

        table <- ball

        <- wait

    } else {

        println("I will build " + key)

        ball[key] = make(chan bool)


        // Throw away ball

        table <- ball


        // Building value

        time.Sleep(time.Millisecond * 10)

        println("I built value for " + key + "!")


        // Clean up ball

        ball = <- table

        close(ball[key])

        delete(ball, key)

        table <- ball

    }


    println("Now value for " + key + " has been built")

}


func main(){

    go keeper()


    ball := make(map[string]chan bool)

    table <- ball


    key := "key"

    go getResource(key)

    go getResource(key)

    go getResource(key)


    time.Sleep(time.Second)

}



查看完整回答
反对 回复 2022-04-26
?
浮云间

TA贡献1829条经验 获得超4个赞

这是一个简单的代码,可以满足您的需求。我对其进行了测试,它可以正常工作。Go 比赛条件检查器没有检测到任何问题。


type Cache struct {

    mtx sync.RWMutex

    m map[KeyType]*CacheValue

}


type CacheValue struct {

    val *ValueType

    mtx sync.Mutex

}


func NewCache() *Cache {

    return &Cache{m: make(map[KeyType]*CacheValue)}

}


func (c *Cache) Get(key KeyType) *ValueType {

    c.mtx.RLock()

    v := c.m[key]

    c.mtx.RUnlock()

    if v != nil {

        v.mtx.Lock()

        x := v.val

        v.mtx.Unlock()

        if x != nil {

            return x

        }

    }


    if v == nil {

        c.mtx.Lock()

        v = c.m[key]

        if v == nil {

            v = &CacheValue{}

            c.m[key] = v

        }

        c.mtx.Unlock()

    }


    v.mtx.Lock()

    if v.val == nil {

        v.val = buildValue(key)

    }

    v.mtx.Unlock()

    return v.val

}


查看完整回答
反对 回复 2022-04-26
  • 3 回答
  • 0 关注
  • 102 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信