我应该在RLock映射之前范围吗? [英] Should I RLock map before range?

查看:155
本文介绍了我应该在RLock映射之前范围吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果多个goroutine将运行notifyAll func,是否可以安全地使用范围映射而不锁定?实际上在一个范围,我需要有时从地图中删除条目。

Is it safe to range map without locking if multiple goroutines will run notifyAll func? Actually in a range I need to sometimes remove entries from a map.

var mu sync.RWMutex

func (self *Server) notifyAll(event *Event)
  ch := make(chan int, 64)
  num := 0
  for k, v := range self.connections {
    num++
    ch <- num

    go func(int k, conn *Conn) {
      err := conn.sendMessage(event)
      <-ch
      if err != nil {
        self.removeConn(k)
      }
    }(k, v)
  }
}

func (self *Server) removeConn(int k) {
  mu.Lock()
  defer mu.Unlock()
  delete(self.connections, k)
}

// Somewhere in another goroutine
func (self *Server) addConn(conn *Conn, int k) {
  mu.Lock()
  defer mu.Unlock()
  self.connections[k] = conn
}


$ b b

还是我必须在范围之前RLock映射?

Or I must RLock map before range?

func (self *Server) notifyAll(event *Event)
  mu.RLock()
  defer mu.RUnlock()
  // Skipped previous body...
}


推荐答案

简单答案:地图在Go中不是并发安全的(仍然可以说线程安全)。

Short answer: maps are not concurrent-safe (one can still say thread-safe) in Go.

所以,如果你需要从不同的go例程访问一个地图,你必须使用某种形式的访问协调,否则不受控制的地图访问会崩溃程序(见 this )。

So, if you need to access a map from different go-routines, you must employ some form of access orchestration, otherwise "uncontrolled map access can crash the program" (see this).

编辑

这是另一种实现,quit,log等等),它忽略互斥量,并使用更多的Goish方法(这只是为了演示这种方法,这有助于我们清除访问编排问题 - 可能是正确的或不适合你的情况):

This is another implementation (without considering housekeeping concerns - timeouts, quit, log, etc) which ignores the mutex all-together and uses a more Goish approach (this is just for demonstrating this approach which helps us to clear access orchestration concerns - might be right or not for your case):

type Server struct {
    connections map[*Conn]struct{}

    _removeConn, _addConn chan *Conn
    _notifyAll            chan *Event
}

func NewServer() *Server {
    s := new(Server)
    s.connections = make(map[*Conn]struct{})
    s._addConn = make(chan *Conn)
    s._removeConn = make(chan *Conn, 1)
    s._notifyAll = make(chan *Event)
    go s.agent()
    return s
}

func (s *Server) agent() {
    for {
        select {
        case c := <-s._addConn:
            s.connections[c] = struct{}{}
        case c := <-s._removeConn:
            delete(s.connections, c)
        case e := <-s._notifyAll:
            for c := range s.connections {
                closure := c
                go func() {
                    err := closure.sendMessage(e)
                    if err != nil {
                        s._removeConn <- closure
                    }
                }()
            }
        }
    }
}

func (s *Server) removeConn(c *Conn) {
    s._removeConn <- c
}

func (s *Server) addConn(c *Conn) {
    s._addConn <- c
}

编辑

我修正了;根据 Damian Gryski 地图可以安全地进行并发读取。每次迭代时映射顺序改变的原因是为映射迭代顺序选择的随机种子,这是goroutine迭代的局部(另一个 tweet )。此事实不会影响第一个编辑和建议的解决方案。

I stand corrected; according to Damian Gryski maps are safe for concurrent reads. The reason that the map order changes on each iteration is "the random seed chosen for map iteration order, which is local to the goroutine iterating" (another tweet of him). This fact does not affect the first edit and suggested solution.

这篇关于我应该在RLock映射之前范围吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆