英文:
Go - high performance communication between goroutines?
问题
我正在尝试用Go语言编写一个小型游戏服务器。我基本上是从WebSocket库(https://github.com/gorilla/websocket)中复制了一个示例。
服务器每秒执行一次s.tick()
函数,将当前时间发送给用户。当用户不执行任何操作时,这个功能正常工作。
当用户加入、离开或发送消息时,信息会通过三个通道之一发送,并执行相应的操作,但s.tick()
要么被跳过,要么延迟。我注意到,如果用户一直执行操作,s.tick()
将永远不会被调用。
以下是输出:
Mon, 05 Dec 2016 20:19:11 CET
Mon, 05 Dec 2016 20:19:12 CET
Mon, 05 Dec 2016 20:19:13 CET
Mon, 05 Dec 2016 20:19:15 CET
Mon, 05 Dec 2016 20:19:16 CET
Mon, 05 Dec 2016 20:19:17 CET
-- 用户加入和离开
Mon, 05 Dec 2016 20:19:25 CET
Mon, 05 Dec 2016 20:19:26 CET
Mon, 05 Dec 2016 20:19:27 CET
Mon, 05 Dec 2016 20:19:28 CET
Mon, 05 Dec 2016 20:19:29 CET
我试图检查是什么原因导致操作之间有这么大的延迟(这些操作都不需要很长时间),我唯一的想法是:
- 我的代码可能有问题(很可能)
- Go的通道速度较慢
我在循环开始和case子句开始时检查了时间,从通道接收数据通常需要超过200毫秒。话虽如此,我该如何提高这个解决方案的性能?我似乎无法让服务器以每秒一次的速度正常工作,更不用说每秒60次了。
以下是我的代码片段:
type GameServer struct {
players map[*Player]bool
register chan *Player
unregister chan *Player
broadcast chan []byte
}
func (s *GameServer) broadcastMessage(msg []byte) {
for player := range s.players {
player.messages <- msg
}
}
func (s *GameServer) tick() {
s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
}
// 问题主要与这个函数有关
func (s *GameServer) run() {
for {
select {
case _ = <-time.NewTicker(time.Second).C:
s.tick()
case client := <-s.register:
s.players[client] = true
case client := <-s.unregister:
delete(s.players, client)
case msg := <-s.broadcast:
s.broadcastMessage(msg)
}
}
}
英文:
I am trying to write a small game server in Go. I basically copied example from WebSocket library (https://github.com/gorilla/websocket).
Every second, servers executes function s.tick()
which sends current time to the user. This works fine when users do not perform any actions.
When a user joins, leaves or sends a message, information is send over one of three channels (one for each action) and corresponding action is executed, however s.tick()
is either skipping or delayed. I have noticed, that if users perform actions all the time, s.tick()
will never be called.
Here's the output:
Mon, 05 Dec 2016 20:19:11 CET
Mon, 05 Dec 2016 20:19:12 CET
Mon, 05 Dec 2016 20:19:13 CET
Mon, 05 Dec 2016 20:19:15 CET
Mon, 05 Dec 2016 20:19:16 CET
Mon, 05 Dec 2016 20:19:17 CET
-- users joining and leaving
Mon, 05 Dec 2016 20:19:25 CET
Mon, 05 Dec 2016 20:19:26 CET
Mon, 05 Dec 2016 20:19:27 CET
Mon, 05 Dec 2016 20:19:28 CET
Mon, 05 Dec 2016 20:19:29 CET
I tried to check what is causing so much delay between the actions (none of these actions is taking long time) and my only thought is that:
- my code is just bad (very likely)
- Go's channels are slow
I checked time at the beginning of a loop and at the beginning of a case clause and it is often taking more than 200ms to even receive data from channel. Having said that, how can I improve the performance of this solution? I can't seem to be able to have server working well at one tick per second, let alone 60.
Below's snippet of my code:
type GameServer struct {
players map[*Player]bool
register chan *Player
unregister chan *Player
broadcast chan []byte
}
func (s *GameServer) broadcastMessage(msg []byte) {
for player := range s.players {
player.messages <- msg
}
}
func (s *GameServer) tick() {
s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
}
// question is mostly related to this function
func (s *GameServer) run() {
for {
select {
case _ = <-time.NewTicker(time.Second).C:
s.tick()
case client := <-s.register:
s.players[client] = true
case client := <-s.unregister:
delete(s.players, client)
case msg := <-s.broadcast:
s.broadcastMessage(msg)
}
}
}
答案1
得分: 3
请尝试以下代码:
type GameServer struct {
players map[*Player]bool
register chan *Player
unregister chan *Player
broadcast chan []byte
ticker *time.Ticker // 使用一个“全局”的*time.Ticker
}
func (s *GameServer) broadcastMessage(msg []byte) {
for player := range s.players {
player.messages <- msg
}
}
func (s *GameServer) tick() {
s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
}
// 问题主要与此函数相关
func (s *GameServer) run() {
for {
select {
case _ = <-s.ticker.C: // 使用“全局”的*time.Ticker,而不是每次都创建一个新的
s.tick()
case client := <-s.register:
s.players[client] = true
case client := <-s.unregister:
delete(s.players, client)
case msg := <-s.broadcast:
s.broadcastMessage(msg)
}
}
}
我认为你不希望在每次调用run
时都创建一个新的time.Ticker
。上述代码应该可以解决你的问题。
英文:
try this:
type GameServer struct {
players map[*Player]bool
register chan *Player
unregister chan *Player
broadcast chan []byte
ticker *time.Ticker //use a single "global" *time.Ticker
}
func (s *GameServer) broadcastMessage(msg []byte) {
for player := range s.players {
player.messages <- msg
}
}
func (s *GameServer) tick() {
s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
}
// question is mostly related to this function
func (s *GameServer) run() {
for {
select {
case _ = <- s.Ticker.C: //use the "global" *time.Ticker instead of creating a new one every time
s.tick()
case client := <-s.register:
s.players[client] = true
case client := <-s.unregister:
delete(s.players, client)
case msg := <-s.broadcast:
s.broadcastMessage(msg)
}
}
}
I don't think you want to be creating a new time.Ticker every call to run
. The above should fix your problems.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论