2016-12-05 16 views
2

私はGoに小さなゲームサーバーを作成しようとしています。私は基本的にWebSocketライブラリ(https://github.com/gorilla/websocket)からサンプルをコピーしました。Go - ゴルーチン間の高性能通信?

毎秒、サーバは機能s.tick()を実行し、現在時刻をユーザに送信します。これは、ユーザーが操作を実行していないときにうまく動作します。

ユーザが参加、メッセージを送信、またはメッセージを送信すると、情報は3つのチャネルの1つ(各アクションに1つ)で送信され、対応するアクションが実行されますが、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 

私はアクション間そんなに遅延を引き起こしているかどうか確認してみました(これらのアクションのどれも時間がかかっていない)と私の唯一の思想があること:

  • 私のコードは、ゴーのチャンネルが

I cが遅い

  • ちょうど悪い(非常に高い)でありますループの開始時とケース節の開始時に時間がかかり、チャネルからデータを受信するのに200ms以上かかることがよくあります。このように言えば、このソリューションのパフォーマンスをどうすれば改善できますか?

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

    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) 
         } 
        } 
    } 
    
  • +4

    GameServerの構造体にグローバルタイムティッカーを追加する必要があります。これは、初期化されるのは一度だけです。最初の選択ケースを 's.ticker.C'に変更してください。現時点では、run関数が呼び出されるたびに新しいティッカーを作成しています。したがって、runが呼び出されてから少なくとも1秒間、他のチャネルにデータがないインスタンスでtickが実行されます。見ている。 – 0xor1

    +2

    あなたはすべての反復で新しいティッカーを作成していますので、安定したティック間隔を持たずにリソースを漏らしてしまいます。 – JimB

    答えて

    3

    はこれを試してみてください。私は一人で自分のコードののスニペットの下に60

    を聞かせて、毎秒1ティックでうまく機能し、サーバを持つことができるように見えることはできません私はrunへのすべての呼び出しを新しいtime.Tickerを作成したいとは思わない。上記はあなたの問題を解決するはずです。

    +0

    しかし、私は 'run'を一度呼び出すだけで、プロセスが終了するまでループします。 –

    +1

    まだforループが終了するたびに、新しいティッカーを作成していますが、どちらの方法でも、テロップ作成をforループの外側に移動するだけで、バグの原因となるすべての繰り返しがティッカーになります。 – 0xor1

    +0

    それは、実際に動作しました。私はトップにあなたのコメントを誤解しました。ありがとうございました :) –