【问题标题】:How can I generalize this map + sync.RWMutex pattern?如何概括此地图 + sync.RWMutex 模式?
【发布时间】:2018-08-02 15:28:33
【问题描述】:

考虑到 go 中没有泛型,我如何概括这种地图访问/修改模式?

func (pool *fPool) fetch(url string) *ResultPromise {
    pool.cacheLock.RLock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.RUnlock()
        return rp
    }
    pool.cacheLock.RUnlock()
    pool.cacheLock.Lock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.Unlock()
        // Skip adding url if someone snuck it in between RUnlock an Lock
        return rp
    }
    rp := newPromise()
    pool.cache[url] = rp
    pool.cacheLock.Unlock()
    pool.c <- fetchWork{rp, url} // Expensive/atomic work
    return rp
}

出于显而易见的原因,它用于多种不同类型的地图。

也许这是一个糟糕的解决方案?

免责声明:与 How to test unlikely concurrent scenarios? 的代码相同,抱歉。

【问题讨论】:

  • 在 Go 中“泛化”只有两种方式,实际上是相同的方式:接口。您可以定义一个表示您需要存储的类型的共享行为的接口,或者您可以使用一个空接口,然后在接收端对它进行类型断言,或者对它进行类型切换。断言/切换到的类型可以是具体类型(例如*ResultPromise),也可以是表示该类型(或多个类型)方法集的接口,这也允许模拟。
  • 为了扩展上面的内容,看起来 fetch 想要处理的类型是一个带有-RLock()、RUnlock()、Lock()、Unlock()、Get()、Put 的接口(),分配更多()。接口类型不能是接收者,因此 fetch 将从方法变为函数。当然,还有其他方法可以进一步抽象,但您可以从那里开始。
  • @JonahB 但那是在谈论用不同的数据结构替换地图。我想对*ResultPromise 以外的事物的地图执行此操作。这意味着 Put() 的参数类型和 Get()newPromise()fetch() 的返回类型本身会发生变化。
  • @Kaedys 我是新手,你可能已经注意到了,但我认为这可以通过代码生成来解决?
  • 可以,但是代码生成通常比较脆弱。需要“通用”缓存等的通常解决方案是使用空接口返回,然后在接收端键入 assert,或者将缓存 Get 调用包装在执行类型断言并转换类型断言失败的助手中改为error 类型。甚至标准库uses this style

标签: dictionary generics go mutex


【解决方案1】:

既然泛型可用,我决定重新访问 Go 并回答我的问题。这是我想出的:

type Cached[K comparable, R any] struct {
    cache map[K]R
    fn    func(K) R
    lock  sync.RWMutex
}

func NewCached[K comparable, R any](
    fn func(K) R) *Cached[K, R] {
    return &Cached[K, R]{
        cache: make(map[K]R),
        fn:    fn,
        lock:  sync.RWMutex{},
    }
}

func (c *Cached[K, R]) Get(k K) R {
    c.lock.RLock()
    if rp, pres := c.cache[k]; pres {
        c.lock.RUnlock()
        return rp
    }
    c.lock.RUnlock()
    c.lock.Lock()
    defer c.lock.Unlock()
    if rp, pres := c.cache[k]; pres {
        // Skip adding key if someone else snuck it in between RUnlock and Lock
        return rp
    }
    rp := c.fn(k)
    c.cache[k] = rp
    return rp
}

Playground
(Currently linking to ?v=gotip and not release because generics still aren't released or something? idk)

previous

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-19
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多