2014-12-26 45 views
0

所以我使用大猩猩websocket庫,我建立一個websocket服務器,當我收到一個連接,我創建2去例程一讀取來自客戶端的傳入消息和另一個1監聽發送到該頻道的消息,然後將其發送給客戶端。golang 2 go例程如果一個終止終止另一個

func (p *Player) EventLoop() { 
    l4g.Info("Starting player %s event loop", p) 
    go p.readFromSocket() 
    go p.writeToSocket() 

    //blocks until we receive an interrupt from the read channel 
    <-p.closeEventChan 
    p.cleanup() //nothing else to do so lets cleanup 
} 

func (p *Player) writeToSocket() { 
    for m := range p.writeChan { 
     if p.conn == nil { 
      break 
     } 

     if reflect.DeepEqual(network.Packet{}, m) == true { 
      break 
     } 

     if err := p.conn.WriteJSON(m); err != nil { 
      break 
     } 
    } 
    p.closeEventChan <- true 
} 

func (p *Player) readFromSocket() { 
    for { 
     if p.conn == nil { 
      break 
     } 

     m := network.Packet{} 

     if err := p.conn.ReadJSON(m); err != nil { 
      break 
     } 
    } 
    p.closeEventChan <- true 
} 

func (p *Player) Cleanup() { 

    //make sure the channels get a close message which will break us out of the go routines 
    close(p.writeChan) 
    close(p.closeEventChan) 

    //only close the connection if we have a connection 
    if p.conn != nil { 
     p.conn.Close() 
     p.conn = nil 
    } 
} 

我的問題是,如果我們離開readFromSocket()Cleanup()被稱爲但是我們從來沒有離開writeToSocket()循環!這個問題可以更簡單地演示在這個去操場https://play.golang.org/p/49bh7bbbG-

我們該如何解決這個問題,所以如果我們離開writeToSocket()循環,我們也離開readFromSocket()循環和副vesa?

我的印象是,如果你調用close通道(close(p.writeChan))的默認值,該通道接收將會收到一封關於此會的工作

+1

該程序只在消息寫入之前退出。 – Arjan

+0

這可能會導致內存泄漏?當我們在去程序終止之前退出函數? (當整個程序終止時,這在操場示例中顯然不是問題),還是會爲我處理這些問題? – zidsal

+1

在播放示例no中,但在您的主要示例中存在問題,因爲您向「closeEventChan」發送了一個值兩次,如果該「chan」沒有被緩存,則其中一個將永遠阻止發送。 – Arjan

回答

2

,通常可以用一個共享的退出渠道做到這一點,一些計數。

func (p *Player) writeToSocket(quit <-chan struct{}) 
    defer func() { 
     p.closeEventChan <- true 
    }() 
    for { 
     select { 
     case <-quit: 
      return 
     case m := <-p.writeChan: 
      // Do stuff 
     // Case where writeChan is closed, but quit isn't 
     default: 
      return 
     } 
    } 
} 


func (p *Player) readFromSocket(quit <-chan struct{}) 
    defer func() { 
     p.closeEventChan <- true 
    }() 
    for { 
     select { 
     case <-quit: 
      return 
     default: 
      // Do stuff 
     } 
    } 
} 

func (p *Player) EventLoop() { 
    l4g.Info("Starting player %s event loop", p) 
    quit := make(chan struct{}) 
    go p.readFromSocket(quit) 
    go p.writeToSocket(quit) 

    //blocks until we receive an interrupt from the read channel 
    <-p.closeEventChan 
    close(quit) 
    // This is superfluous here, but in a different case 
    // you loop over the remaining number of workers using a NumWorkers variable 
    for i := 0; i < 1; i++ { 
     <-p.closeEventChan 
    } 
    p.cleanup() //nothing else to do so lets cleanup 
} 

的這裏的想法是:

  1. 所有工人在返回前在經由通道關閉通知中央程序。
  2. 所有員工在退出正在廣播的消息時(通過關閉)返回。
  3. 中央循環在第一個工人退出後關閉退出通道,然後等待其餘退出。