dongyi2425 2017-08-10 22:04
浏览 60

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

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:

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条回答 默认 最新

  • dtnd30892 2017-08-11 08:30
    关注

    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.

    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器