Go – 高性能的 goroutine 之间的通信?

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

Go - high performance communication between goroutines?

问题

我正在尝试用Go语言编写一个小型游戏服务器。我基本上是从WebSocket库(https://github.com/gorilla/websocket)中复制了一个示例。

服务器每秒执行一次s.tick()函数,将当前时间发送给用户。当用户不执行任何操作时,这个功能正常工作。

当用户加入、离开或发送消息时,信息会通过三个通道之一发送,并执行相应的操作,但s.tick()要么被跳过,要么延迟。我注意到,如果用户一直执行操作,s.tick()将永远不会被调用。

以下是输出:

  1. Mon, 05 Dec 2016 20:19:11 CET
  2. Mon, 05 Dec 2016 20:19:12 CET
  3. Mon, 05 Dec 2016 20:19:13 CET
  4. Mon, 05 Dec 2016 20:19:15 CET
  5. Mon, 05 Dec 2016 20:19:16 CET
  6. Mon, 05 Dec 2016 20:19:17 CET
  7. -- 用户加入和离开
  8. Mon, 05 Dec 2016 20:19:25 CET
  9. Mon, 05 Dec 2016 20:19:26 CET
  10. Mon, 05 Dec 2016 20:19:27 CET
  11. Mon, 05 Dec 2016 20:19:28 CET
  12. Mon, 05 Dec 2016 20:19:29 CET

我试图检查是什么原因导致操作之间有这么大的延迟(这些操作都不需要很长时间),我唯一的想法是:

  • 我的代码可能有问题(很可能)
  • Go的通道速度较慢

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

以下是我的代码片段:

  1. type GameServer struct {
  2. players map[*Player]bool
  3. register chan *Player
  4. unregister chan *Player
  5. broadcast chan []byte
  6. }
  7. func (s *GameServer) broadcastMessage(msg []byte) {
  8. for player := range s.players {
  9. player.messages <- msg
  10. }
  11. }
  12. func (s *GameServer) tick() {
  13. s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
  14. }
  15. // 问题主要与这个函数有关
  16. func (s *GameServer) run() {
  17. for {
  18. select {
  19. case _ = <-time.NewTicker(time.Second).C:
  20. s.tick()
  21. case client := <-s.register:
  22. s.players[client] = true
  23. case client := <-s.unregister:
  24. delete(s.players, client)
  25. case msg := <-s.broadcast:
  26. s.broadcastMessage(msg)
  27. }
  28. }
  29. }
英文:

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:

  1. Mon, 05 Dec 2016 20:19:11 CET
  2. Mon, 05 Dec 2016 20:19:12 CET
  3. Mon, 05 Dec 2016 20:19:13 CET
  4. Mon, 05 Dec 2016 20:19:15 CET
  5. Mon, 05 Dec 2016 20:19:16 CET
  6. Mon, 05 Dec 2016 20:19:17 CET
  7. -- users joining and leaving
  8. Mon, 05 Dec 2016 20:19:25 CET
  9. Mon, 05 Dec 2016 20:19:26 CET
  10. Mon, 05 Dec 2016 20:19:27 CET
  11. Mon, 05 Dec 2016 20:19:28 CET
  12. 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:

  1. type GameServer struct {
  2. players map[*Player]bool
  3. register chan *Player
  4. unregister chan *Player
  5. broadcast chan []byte
  6. }
  7. func (s *GameServer) broadcastMessage(msg []byte) {
  8. for player := range s.players {
  9. player.messages &lt;- msg
  10. }
  11. }
  12. func (s *GameServer) tick() {
  13. s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
  14. }
  15. // question is mostly related to this function
  16. func (s *GameServer) run() {
  17. for {
  18. select {
  19. case _ = &lt;-time.NewTicker(time.Second).C:
  20. s.tick()
  21. case client := &lt;-s.register:
  22. s.players[client] = true
  23. case client := &lt;-s.unregister:
  24. delete(s.players, client)
  25. case msg := &lt;-s.broadcast:
  26. s.broadcastMessage(msg)
  27. }
  28. }
  29. }

答案1

得分: 3

请尝试以下代码:

  1. type GameServer struct {
  2. players map[*Player]bool
  3. register chan *Player
  4. unregister chan *Player
  5. broadcast chan []byte
  6. ticker *time.Ticker // 使用一个“全局”的*time.Ticker
  7. }
  8. func (s *GameServer) broadcastMessage(msg []byte) {
  9. for player := range s.players {
  10. player.messages <- msg
  11. }
  12. }
  13. func (s *GameServer) tick() {
  14. s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
  15. }
  16. // 问题主要与此函数相关
  17. func (s *GameServer) run() {
  18. for {
  19. select {
  20. case _ = <-s.ticker.C: // 使用“全局”的*time.Ticker,而不是每次都创建一个新的
  21. s.tick()
  22. case client := <-s.register:
  23. s.players[client] = true
  24. case client := <-s.unregister:
  25. delete(s.players, client)
  26. case msg := <-s.broadcast:
  27. s.broadcastMessage(msg)
  28. }
  29. }
  30. }

我认为你不希望在每次调用run时都创建一个新的time.Ticker。上述代码应该可以解决你的问题。

英文:

try this:

  1. type GameServer struct {
  2. players map[*Player]bool
  3. register chan *Player
  4. unregister chan *Player
  5. broadcast chan []byte
  6. ticker *time.Ticker //use a single &quot;global&quot; *time.Ticker
  7. }
  8. func (s *GameServer) broadcastMessage(msg []byte) {
  9. for player := range s.players {
  10. player.messages &lt;- msg
  11. }
  12. }
  13. func (s *GameServer) tick() {
  14. s.broadcastMessage([]byte(time.Now().Format(time.RFC1123)))
  15. }
  16. // question is mostly related to this function
  17. func (s *GameServer) run() {
  18. for {
  19. select {
  20. case _ = &lt;- s.Ticker.C: //use the &quot;global&quot; *time.Ticker instead of creating a new one every time
  21. s.tick()
  22. case client := &lt;-s.register:
  23. s.players[client] = true
  24. case client := &lt;-s.unregister:
  25. delete(s.players, client)
  26. case msg := &lt;-s.broadcast:
  27. s.broadcastMessage(msg)
  28. }
  29. }
  30. }

I don't think you want to be creating a new time.Ticker every call to run. The above should fix your problems.

huangapple
  • 本文由 发表于 2016年12月6日 03:37:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/40981992.html
匿名

发表评论

匿名网友

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

确定