【问题标题】:Go - high performance communication between goroutines?Go - goroutines之间的高性能通信?
【发布时间】:2016-12-05 19:37:38
【问题描述】:

我正在尝试用 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

-- 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

我试图检查是什么导致了操作之间的如此多的延迟(这些操作都不会花费很长时间),我唯一的想法是:

  • 我的代码很糟糕(很可能)
  • Go 的频道很慢

我检查了循环开始和 case 子句开始的时间,甚至从通道接收数据通常需要超过 200 毫秒。话虽如此,我该如何提高此解决方案的性能?我似乎无法让服务器以每秒 1 个滴答的速度正常工作,更不用说 60 次了。

下面是我的代码的sn-p:

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)
        }
    }
}

【问题讨论】:

  • 我建议你需要在你的 GameServer 结构上添加一个全局时间 Ticker,它只被初始化一次,并将你的第一个选择案例更改为 s.ticker.C。目前,您每次调用 run 函数时都会创建一个新的ticker,因此它只会在调用 run 后至少一秒钟内没有其他通道在其中包含任何数据的实例中执行 tick,这解释了您的行为正在看。
  • 您在每次迭代时都会创建一个新的 Ticker,因此您会在没有稳定的滴答间隔的情况下泄漏资源。

标签: go concurrency goroutine


【解决方案1】:

试试这个:

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)
        }
    }
}

我不认为你想创造一个新的时间。每次调用 run 时都记下。以上应该可以解决您的问题。

【讨论】:

  • 但是我只调用run 一次然后它循环直到进程被杀死。
  • 仍然每次 for 循环结束时,您都在创建一个新的代码,您可以将代码创建移到 for 循环之外,无论哪种方式,它都会在每次迭代时创建代码,这是导致你的错误。
  • 知道了,确实有效。我误解了你在上面的评论。谢谢你:)
猜你喜欢
  • 2015-08-09
  • 2021-08-20
  • 1970-01-01
  • 1970-01-01
  • 2013-01-12
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
相关资源
最近更新 更多