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

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

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

Go
翻翻过去那场雪 2023-08-14 14:53:53
寻找更 Go 式的解决方案来解决以下问题:假设服务器有多个并行传入请求,要求使用 key 的资源key。由于计算此资源非常昂贵/耗时,因此我们希望确保仅计算一次。有无限多个可能的键。一种简单的实现:if hasCachedValue(key) {   return cachedValue(key)}if somebodyElseWorkingOn(key) {   waitUntilReady(key)} else {   buildCacheValue(key) // time consuming}return cachedValue(key)到目前为止,我们已经使用共享解决了这个问题map[string]chan bool,其中第一个请求插入了 chan for key,而后续请求在值准备好时等待该 chan 的关闭。为了保护地图,我们使用了sync.Mutex,但我们感觉有一个更好、更 Go 式的解决方案。
查看完整描述

3 回答

?
波斯汪

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

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

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


查看完整回答
反对 回复 2023-08-14
?
肥皂起泡泡

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

这是一个简单的代码,可以完成您想要的操作。我测试了它,它工作没有问题。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

}


查看完整回答
反对 回复 2023-08-14
?
梵蒂冈之花

TA贡献1900条经验 获得超5个赞

受到经常用于描述通道的乒乓球示例的启发,我们尝试了仅通道方法。球保存有关正在生成的密钥的状态,并且球在共享通道的请求之间传递:


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)

}


查看完整回答
反对 回复 2023-08-14
  • 3 回答
  • 0 关注
  • 151 浏览
慕课专栏
更多

添加回答

举报

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