这个并发地图有什么问题?

时间:2016-10-19 11:46:37

标签: dictionary go concurrency

最近我开发了一个golang TCP网络编程框架名称Tao,在文件util.go中有一个名为 ConnectionMap 的并发映射,我用它来管理传入的TCP连接,它是由多个go-routines读取和写入的int64-to-Connection映射。

然后我开发了一个基于Tao的远程控制系统,移动应用程序可以通过该系统控制设备。但是我发现 ConnectionMap 有问题:某些已经关闭的连接不会从此地图中删除并保持存在。

我不太确定这是一段时间之后应用程序很难连接到这个系统的原因,但我真的很困惑这是写出一个并发映射的正确方法吗?是不是错了? 谢谢

type ConnectionMap struct{
  sync.RWMutex
  m map[int64]Connection
}

func NewConnectionMap() *ConnectionMap {
  return &ConnectionMap{
    m: make(map[int64]Connection),
  }
}

func (cm *ConnectionMap)Clear() {
  cm.Lock()
  cm.m = make(map[int64]Connection)
  cm.Unlock()
}

func (cm *ConnectionMap)Get(k int64) (Connection, bool) {
  cm.RLock()
  conn, ok := cm.m[k]
  cm.RUnlock()
  return conn, ok
}

func (cm *ConnectionMap)IterKeys() <-chan int64 {
  kch := make(chan int64)
  go func() {
    cm.RLock()
    for k, _ := range cm.m {
      kch<- k
    }
    cm.RUnlock()
    close(kch)
  }()
  return kch
}

func (cm *ConnectionMap)IterValues() <-chan Connection {
  vch := make(chan Connection)
  go func() {
    cm.RLock()
    for _, v := range cm.m {
      vch<- v
    }
    cm.RUnlock()
    close(vch)
  }()
  return vch
}

func (cm *ConnectionMap)Put(k int64, v Connection) {
  cm.Lock()
  cm.m[k] = v
  cm.Unlock()
}

func (cm *ConnectionMap)Remove(k int64) {
  cm.Lock()
  delete(cm.m, k)
  cm.Unlock()
}

func (cm *ConnectionMap)Size() int {
  cm.RLock()
  size := len(cm.m)
  cm.RUnlock()
  return size
}

func (cm *ConnectionMap)IsEmpty() bool {
  return cm.Size() <= 0
}

1 个答案:

答案 0 :(得分:1)

如果使用不当,

IterKeysIterValues可以阻止所有作者。您正在使用非缓冲通道并锁定整个地图,直到读取所有值。请记住,只有在释放所有读锁后才能获取写锁。任何不会耗尽通道的调用者都会在读取锁定时泄漏goroutine。

我能想到多种解决方案:

  1. 确保所有来电者尽快消耗频道。
  2. 根本不要使用goroutines。同时迭代地图,同时保持读锁定。
  3. 您可以使用两个通道使锁定更精细。一个将表示您想要读取值,第二个将锁定地图,读取值,解锁地图并发送值。这样,调用者可以在不锁定整个地图的情况下花费他想要的时间。
  4. 我赞成第二种解决方案。它更容易实现,更容易理解和使用。第一个太脆弱了,而第三个太复杂了,可能没必要。它也会慢得多。