我想用Go实现一个单例。普通单例之间的区别在于实例是单例,在映射结构中具有不同的键。类似于此代码。我不确定演示代码是否存在任何数据竞争。
var instanceLock sync.Mutex
var instances map[string]string
func getDemoInstance(key string) string {
if value, ok := instances[key]; ok {
return value
}
instanceLock.Lock()
defer instanceLock.Unlock()
if value, ok := instances[key]; ok {
return value
} else {
instances[key] = key + key
return key + key
}
}
是的,存在数据竞赛,您可以通过使用go run -race main.go
运行它来确认。如果一个 goroutine 锁定并修改了映射,则另一个 goroutine 可能在锁定之前读取它。
您可以在读取时使用sync.RWMutex
进行读锁定(允许多个读取器而不会相互阻止(。
例如:
var (
instancesMU sync.RWMutex
instances = map[string]string{}
)
func getDemoInstance(key string) string {
instancesMU.RLock()
if value, ok := instances[key]; ok {
instancesMU.RUnlock()
return value
}
instancesMU.RUnlock()
instancesMU.Lock()
defer instancesMU.Unlock()
if value, ok := instances[key]; ok {
return value
}
value := key + key
instances[key] = value
return value
}
你也可以试试这个:sync。地图
Map 类似于 Go map[interface{}]interface{},但可以安全地由多个 goroutines 并发使用,无需额外的锁定或协调。加载、存储和删除在摊销的常量时间内运行。
Map 类型针对两种常见用例进行了优化:(1( 当给定键的条目只写入一次但读取多次时,例如在仅增长的缓存中,或 (2( 当多个 goroutine读取、写入和覆盖不相交键集的条目时。
在这两种情况下,与与单独的互斥或 RWMutex 配对的 Go 映射相比,使用 Map 可以显著减少锁争用。
注意:在第三段中,它提到了为什么使用sync.Map
是有益的,而不是简单地使用Go Map与sync.RWMutex
配对。
所以这完全适合你的情况,我猜?
回答有点晚了,无论如何这应该会有所帮助:https://github.com/ashwinshirva/api/tree/master/dp/singleton
这显示了实现单一实例的两种方法:
- 使用
sync.Mutex
- 使用
sync.Once