英文:
Thread-safe map of ref-counted resources
问题
管理一组资源的方法如下:
- 通过全局列表(例如哈希映射)按名称访问资源。
- 多个线程同时访问这些资源。
- 使用引用计数(Golang缺乏"弱引用";请参阅https://groups.google.com/forum/#!topic/golang-nuts/PYWxjT2v6ps)。
示例:
var theList tMap // 全局变量
// 在线程A、B、C等中
aThing := theList.ref("aThing") // 如果存在,则引用计数加1;否则插入
// 使用aThing
theList.unref("aThing") // 引用计数减1;如果为0,则移除
编辑:我原本期望能找到一种推荐的模式,但没有找到。因此,我想出了以下解决方案:
type tMap struct {
set map[string]*tMapSet
updt sync.RWMutex
}
type tMapSet struct {
...
refcount int32
}
func (o *tMap) ref(iId string) *tMapSet {
o.updt.RLock()
aSet := o.set[iId]
if aSet != nil {
atomic.AddInt32(&aSet.refcount, 1)
}
o.updt.RUnlock()
if aSet == nil {
o.updt.Lock()
if aTemp := o.set[iId]; aTemp != nil {
aSet = aTemp
aSet.refcount++
} else {
aSet = &tMapSet{refcount:1}
o.set[iId] = aSet
}
o.updt.Unlock()
}
return aSet
}
func (o *tMap) unref(iId string) {
o.updt.RLock()
aSet := o.set[iId]
aN := atomic.AddInt32(&aSet.refcount, -1) // 如果找不到set[iId],则会出错
o.updt.RUnlock()
if aN == 0 {
o.updt.Lock()
if aSet.refcount == 0 {
delete(o.set, iId)
}
o.updt.Unlock()
}
}
对上述代码的清晰度或简洁度有什么改进建议吗?
英文:
On managing a collection of resources which are:
- accessible by name through a global list (e.g. a hash-map)
- accessed concurrently from multiple threads
- ref-counted (Golang lacks "weak references"; see
https://groups.google.com/forum/#!topic/golang-nuts/PYWxjT2v6ps)
Example:
var theList tMap // global
// in thread A, B, C etc
aThing := theList.ref("aThing") // if exists then refcount++, else insert
// use aThing
theList.unref("aThing") // refcount--, if 0 then remove
Edit: I expected to find a recommended pattern for this, but nothing turned up. So I came up with this:
type tMap struct {
set map[string]*tMapSet
updt sync.RWMutex
}
type tMapSet struct {
...
refcount int32
}
func (o *tMap) ref(iId string) *tMapSet {
o.updt.RLock()
aSet := o.set[iId]
if aSet != nil {
atomic.AddInt32(&aSet.refcount, 1)
}
o.updt.RUnlock()
if aSet == nil {
o.updt.Lock()
if aTemp := o.set[iId]; aTemp != nil {
aSet = aTemp
aSet.refcount++
} else {
aSet = &tMapSet{refcount:1}
o.set[iId] = aSet
}
o.updt.Unlock()
}
return aSet
}
func (o *tMap) unref(iId string) {
o.updt.RLock()
aSet := o.set[iId]
aN := atomic.AddInt32(&aSet.refcount, -1) // crash if set[iId] not found
o.updt.RUnlock()
if aN == 0 {
o.updt.Lock()
if aSet.refcount == 0 {
delete(o.set, iId)
}
o.updt.Unlock()
}
}
Suggestions improving the clarity or brevity of the above?
答案1
得分: 1
只需在访问时将您的映射包装在互斥锁中以保护访问(如果读取操作较多且写入操作较少,您也可以使用RWMutex,可能数据应该存储具体类型)。以下是一个示例,具有几个方法:
type MagicMap struct {
sync.Mutex
data map[string]interface{}
counts map[string]int
}
func (m *MagicMap) Get(key string) interface{} {
m.Lock()
defer m.Unlock()
return m.data[key]
}
func (m *MagicMap) Add(key string, value interface{}) {
m.Lock()
m.data[key] = value
m.counts[key] = m.counts[key] + 1
m.Unlock()
}
func (m *MagicMap) Remove(key string) {
m.Lock()
count := m.counts[key]
count -= 1
if count < 1 {
delete(m.data, key)
delete(m.counts, key)
} else {
m.counts[key] = count
}
m.Unlock()
}
这段代码未经测试,写得很快,可能存在错误,但希望能给您提供一个尝试的方向。如果您愿意,您也可以只使用一个map[mystruct]int
来存储结构体和计数。示例中使用了分开的键和值。
这里是另一个使用互斥锁保护映射访问的示例:https://gobyexample.com/mutexes
您还可以在Go 1.9中使用新的sync.Map
,但它比仅使用互斥锁要慢。
英文:
Just wrap your map in a mutex to protect access (you could also use RWMutex if lots of reads and few writes, probably data should store a concrete type too). Something like this with a few methods would be fine:
type MagicMap struct {
sync.Mutex
data map[string]interface{}
counts map[string]int
}
func (m MagicMap) Get(key string) interface{} {
m.Lock()
defer m.Unlock()
return m.data[key]
}
func (m MagicMap) Add(key string, value interface{}) {
m.Lock()
m.data[key] = value
m.counts[key] = m.counts[key] + 1
m.Unlock()
}
func (m MagicMap) Remove(key string) {
m.Lock()
count := m.counts[key]
count -= 1
if count < 1 {
delete(m.data, key)
delete(m.counts, key)
} else {
m.counts[key] = count
}
m.Unlock()
}
This is untested, written quickly, and may be buggy, but hopefully gives you a direction to try out. If you want you could just have one map with map[mystruct]int to store structs and counts. The example has separate keys and values.
https://play.golang.org/p/9k1lNRpqua
Here's another example of using a mutex to protect map access:
https://gobyexample.com/mutexes
You can also use the new sync.Map in Go 1.9 but it's slower than just using a mutex.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论