我研究了大猩猩/ websocket包的Godoc。
在Godoc中明确指出
并发 Connections支持一个并发读者和一个并发编写器。
应用程序负责确保不超过一个goroutine同时调用write方法(NextWriter,SetWriteDeadline,WriteMessage,WriteJSON,EnableWriteCompression,SetCompressionLevel),并且只有一个goroutine调用read方法(NextReader,SetReadDeadline,ReadMessage,同时使用ReadJSON,SetPongHandler,SetPingHandler。
Close和WriteControl方法可以与所有其他方法同时调用 方法
但是,在包中提供的一个例子中
func (c *Conn) readPump() {
defer func() {
hub.unregister <- c
c.ws.Close()
}()
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(pongWait))
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil
})
for {
_, message, err := c.ws.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
log.Printf("error: %v", err)
}
break
}
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
hub.broadcast <- message
}
}
这一行
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil
})
和这一行
_, message, err := c.ws.ReadMessage()
似乎没有同步,因为第一行是一个回调函数,所以它应该在包中创建的Goroutine中调用,第二行在Goroutine中执行,调用serveWs
更重要的是,我应该如何确保不超过一个goroutine同时调用SetReadDeadline
,ReadMessage
,SetPongHandler
,SetPingHandler
?
我试图使用Mutex锁并在我调用上述函数时锁定它,然后解锁它,但很快我就意识到了问题。通常(在该示例中)在for循环中调用ReadMessage
。但是如果Mutext在ReadMessage之前被锁定,那么在收到下一条消息之前,没有其他的Read函数可以获取锁并执行
有没有更好的方法来处理这个并发问题?提前谢谢。
答案 0 :(得分:3)
确保没有对read方法的并发调用的最佳方法是从单个goroutine执行所有读取方法。
所有Gorilla websocket示例都使用此方法,包括粘贴在问题中的示例。在该示例中,对read方法的所有调用都来自readPump
方法。对于单个goroutine上的连接,将调用readPump
方法一次。因此,连接读取方法不会同时调用。
section of the documentation on control messages表示应用程序必须读取与过程控制消息的连接。基于这个和Gorilla自己的例子,我认为可以安全地假设ping,pong和close处理程序将从应用程序的读取goroutine中调用,就像在当前实现中一样。如果文档可以更明确地说明这一点会很好。也许提出问题?