【发布时间】:2017-08-30 17:20:42
【问题描述】:
我研究过gorilla/websocket包的godoc。
在 Godoc 中明确指出
并发 连接支持一个并发读取器和一个并发写入器。
应用程序负责确保不超过一个 goroutine 同时调用写入方法(NextWriter、SetWriteDeadline、WriteMessage、WriteJSON、EnableWriteCompression、SetCompressionLevel),并且不超过一个 goroutine 调用读取方法(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 中调用,第二行在调用serveWs的 Goroutine 中执行
更重要的是,我应该如何确保不超过一个goroutine同时调用SetReadDeadline、ReadMessage、SetPongHandler、SetPingHandler?
我尝试使用互斥锁并在调用上述函数时将其锁定,然后再将其解锁,但很快我就意识到了一个问题。通常(也在示例中)ReadMessage 在 for 循环中被调用。但是如果 Mutext 在 ReadMessage 之前被锁定,那么在收到下一条消息之前,没有其他 Read-functions 可以获取锁并执行
有没有更好的方法来处理这个并发问题?提前致谢。
【问题讨论】:
-
研究了一下包里面,如果我没看错的话,ping和pong回调函数实际上是在调用ReadMessage()的同一个GoRoutine中执行的。所以这个例子仍然保证了并发性。而SetPingHandler()和SetPongHandler()也是需要同步的,但是在例子中是在调用任何ReadMessage()之前调用的,这样就可以了。