Why does gorilla websocket chat example not finding it neccessary to use sync.RWMutex to access and edit maps?

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

Why does gorilla websocket chat example not finding it neccessary to use sync.RWMutex to access and edit maps?

问题

在聊天示例中有一个名为hub.go的文件。

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

我对该文件进行了一些更改,现在它的内容如下:

type hub struct {
	// 已注册的连接
	connections map[int64]*connection

	sync.RWMutex

	// 来自连接的传入消息
	broadcast chan []byte

	// 来自连接的注册请求
	register chan *connection

	// 来自连接的注销请求
	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)
				}
			}
		}
	}
}

我在hub结构体中添加了sync.RWMutex,但我不确定是否有必要。为什么示例中没有包含它?也许我漏掉了什么?在锁定和解锁方面是否过度了?

另外,在init()方法的最后一个case中,我不确定如何进行锁定和解锁,因为它同时进行读取和写入。我应该同时使用Rlock()和Lock()吗?具体应该如何实现?

英文:

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?

答案1

得分: 2

互斥锁不是必需的,因为单个中心协程是唯一访问映射的协程。

另一种方法是消除Go协程和通道,并用使用互斥锁的函数替换它们。

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
	h.mu.Unlock()
}

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 <- message:
		default:
			close(c.send)
			delete(h.connections, c)
		}
	}
	h.mu.Unlock()
}

重要的是使用互斥锁保护close(c.send)c.send <- message,这可以防止在关闭的通道上发送数据。

英文:

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 &lt;- m:
	default:
	  close(c.send)
	  delete(h.connections, c)
	}
  }
  h.mu.Unlock()
}

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

huangapple
  • 本文由 发表于 2015年9月25日 06:36:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/32772057.html
匿名

发表评论

匿名网友

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

确定