2014-12-26 114 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. 中央循环在第一个工人退出后关闭退出通道,然后等待其余退出。