为什么大猩猩websocket聊天示例未找到使用sync.RWMutex来访问和编辑地图的必要?

In the chat example there is a file called hub.go.

https://github.com/gorilla/websocket/blob/master/examples/chat/hub.go

I have made some changes to that file and it looks like this:

type hub struct {
    // Registered connections.
    connections map[int64]*connection

    sync.RWMutex

    // Inbound messages from the connections.
    broadcast chan []byte

    // Register requests from the connections.
    register chan *connection

    // Unregister requests from connections.
    unregister chan *connection
}

var wsHub = hub{
    connections: make(map[int64]*connection),
    broadcast:   make(chan []byte),
    register:    make(chan *connection),
    unregister:  make(chan *connection),
}

func (h *hub) init() {
    for {
        select {
        case c := <-h.register:
            h.Lock()
            h.connections[c.userId] = c
            h.Unlock()
        case c := <-h.unregister:
            h.RLock()
            _, ok := h.connections[c.userId]
            h.RUnlock()
            if ok {
                h.Lock()
                delete(h.connections, c.userId)
                h.Unlock()
                close(c.send)
            }
        case m := <-h.broadcast:
            for _, c := range h.connections {
                select {
                case c.send <- m:
                default:
                    close(c.send)
                    delete(h.connections, c.userId)
                }
            }
        }
    }
}

I have added sync.RWMutex to the hub struct but I am not sure if this is necessary. Why is it not included in the example? Maybe I am missing something? Is it overkill to lock and unlock?

And also the last case in the init() method I'm not sure how to lock and unlock because it reads and writes at the same time. Should I use both the Rlock() and Lock()? How would that look like?

A mutex is not required because the single hub goroutine is the only goroutine that accesses the map.

An alternate approach is to eliminate the Go routine and channels and replace them with functions that use a mutex.

type hub struct {
  connections map[*connection]bool
  mu sync.Mutex
}

var h = hub{
   connections: make(map[*connection]bool),
}

func (h * hub) register(c *connection) {
  h.mu.Lock()
  h.connections[c] = true
}

func (h *hub) unregister(c *connection) {
  h.mu.Lock()
  if _, ok := h.connections[c]; ok {
     delete(h.connections, c)
     close(c.send)
  }
  h.mu.Unlock()
}

func (h * hub) broadcast(message []byte) {
  h.mu.Lock()
  for c := range h.connections {
    select {
    case c.send <- m:
    default:
      close(c.send)
      delete(h.connections, c)
    }
  }
  h.mu.Unlock()
}

It's important to protect close(c.send) and c.send <- m with a mutex. This prevents send on a closed channel.