线程安全的引用计数资源映射

huangapple go评论132阅读模式
英文:

Thread-safe map of ref-counted resources

问题

管理一组资源的方法如下:

  1. 通过全局列表(例如哈希映射)按名称访问资源。
  2. 多个线程同时访问这些资源。
  3. 使用引用计数(Golang缺乏"弱引用";请参阅https://groups.google.com/forum/#!topic/golang-nuts/PYWxjT2v6ps)。

示例:

  1. var theList tMap // 全局变量
  2. // 在线程A、B、C等中
  3. aThing := theList.ref("aThing") // 如果存在,则引用计数加1;否则插入
  4. // 使用aThing
  5. theList.unref("aThing") // 引用计数减1;如果为0,则移除

编辑:我原本期望能找到一种推荐的模式,但没有找到。因此,我想出了以下解决方案:

  1. type tMap struct {
  2. set map[string]*tMapSet
  3. updt sync.RWMutex
  4. }
  5. type tMapSet struct {
  6. ...
  7. refcount int32
  8. }
  9. func (o *tMap) ref(iId string) *tMapSet {
  10. o.updt.RLock()
  11. aSet := o.set[iId]
  12. if aSet != nil {
  13. atomic.AddInt32(&aSet.refcount, 1)
  14. }
  15. o.updt.RUnlock()
  16. if aSet == nil {
  17. o.updt.Lock()
  18. if aTemp := o.set[iId]; aTemp != nil {
  19. aSet = aTemp
  20. aSet.refcount++
  21. } else {
  22. aSet = &tMapSet{refcount:1}
  23. o.set[iId] = aSet
  24. }
  25. o.updt.Unlock()
  26. }
  27. return aSet
  28. }
  29. func (o *tMap) unref(iId string) {
  30. o.updt.RLock()
  31. aSet := o.set[iId]
  32. aN := atomic.AddInt32(&aSet.refcount, -1) // 如果找不到set[iId],则会出错
  33. o.updt.RUnlock()
  34. if aN == 0 {
  35. o.updt.Lock()
  36. if aSet.refcount == 0 {
  37. delete(o.set, iId)
  38. }
  39. o.updt.Unlock()
  40. }
  41. }

对上述代码的清晰度或简洁度有什么改进建议吗?

英文:

On managing a collection of resources which are:

  1. accessible by name through a global list (e.g. a hash-map)
  2. accessed concurrently from multiple threads
  3. ref-counted (Golang lacks "weak references"; see
    https://groups.google.com/forum/#!topic/golang-nuts/PYWxjT2v6ps)

Example:

  1. var theList tMap // global
  2. // in thread A, B, C etc
  3. aThing := theList.ref("aThing") // if exists then refcount++, else insert
  4. // use aThing
  5. 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:

  1. type tMap struct {
  2. set map[string]*tMapSet
  3. updt sync.RWMutex
  4. }
  5. type tMapSet struct {
  6. ...
  7. refcount int32
  8. }
  9. func (o *tMap) ref(iId string) *tMapSet {
  10. o.updt.RLock()
  11. aSet := o.set[iId]
  12. if aSet != nil {
  13. atomic.AddInt32(&aSet.refcount, 1)
  14. }
  15. o.updt.RUnlock()
  16. if aSet == nil {
  17. o.updt.Lock()
  18. if aTemp := o.set[iId]; aTemp != nil {
  19. aSet = aTemp
  20. aSet.refcount++
  21. } else {
  22. aSet = &tMapSet{refcount:1}
  23. o.set[iId] = aSet
  24. }
  25. o.updt.Unlock()
  26. }
  27. return aSet
  28. }
  29. func (o *tMap) unref(iId string) {
  30. o.updt.RLock()
  31. aSet := o.set[iId]
  32. aN := atomic.AddInt32(&aSet.refcount, -1) // crash if set[iId] not found
  33. o.updt.RUnlock()
  34. if aN == 0 {
  35. o.updt.Lock()
  36. if aSet.refcount == 0 {
  37. delete(o.set, iId)
  38. }
  39. o.updt.Unlock()
  40. }
  41. }

Suggestions improving the clarity or brevity of the above?

答案1

得分: 1

只需在访问时将您的映射包装在互斥锁中以保护访问(如果读取操作较多且写入操作较少,您也可以使用RWMutex,可能数据应该存储具体类型)。以下是一个示例,具有几个方法:

  1. type MagicMap struct {
  2. sync.Mutex
  3. data map[string]interface{}
  4. counts map[string]int
  5. }
  6. func (m *MagicMap) Get(key string) interface{} {
  7. m.Lock()
  8. defer m.Unlock()
  9. return m.data[key]
  10. }
  11. func (m *MagicMap) Add(key string, value interface{}) {
  12. m.Lock()
  13. m.data[key] = value
  14. m.counts[key] = m.counts[key] + 1
  15. m.Unlock()
  16. }
  17. func (m *MagicMap) Remove(key string) {
  18. m.Lock()
  19. count := m.counts[key]
  20. count -= 1
  21. if count < 1 {
  22. delete(m.data, key)
  23. delete(m.counts, key)
  24. } else {
  25. m.counts[key] = count
  26. }
  27. m.Unlock()
  28. }

这段代码未经测试,写得很快,可能存在错误,但希望能给您提供一个尝试的方向。如果您愿意,您也可以只使用一个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:

  1. type MagicMap struct {
  2. sync.Mutex
  3. data map[string]interface{}
  4. counts map[string]int
  5. }
  6. func (m MagicMap) Get(key string) interface{} {
  7. m.Lock()
  8. defer m.Unlock()
  9. return m.data[key]
  10. }
  11. func (m MagicMap) Add(key string, value interface{}) {
  12. m.Lock()
  13. m.data[key] = value
  14. m.counts[key] = m.counts[key] + 1
  15. m.Unlock()
  16. }
  17. func (m MagicMap) Remove(key string) {
  18. m.Lock()
  19. count := m.counts[key]
  20. count -= 1
  21. if count &lt; 1 {
  22. delete(m.data, key)
  23. delete(m.counts, key)
  24. } else {
  25. m.counts[key] = count
  26. }
  27. m.Unlock()
  28. }

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.

huangapple
  • 本文由 发表于 2017年8月11日 06:04:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/45624115.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定