【发布时间】: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