golang 2 go例程,如果一个终止,则终止另一个

时间:2014-12-26 20:19:52

标签: go channel goroutine

所以我正在使用gorillas websocket库,我正在构建一个websocket服务器,当我收到一个连接时,我创建了2个例程,一个用于读取来自客户端的传入消息,另一个用于侦听发送到该频道的即将发送的消息然后将它们发送给客户。

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()循环!在这个go playground https://play.golang.org/p/49bh7bbbG-

中可以更简单地证明这个问题

我们如何解决这个问题呢?如果我们离开writeToSocket()循环,我们也会离开readFromSocket()循环和副vesa?

我的印象是,这会像在频道(close(p.writeChan))上呼叫关闭一样,会发送频道接受的默认值

1 个答案:

答案 0 :(得分:3)

您通常可以使用共享退出渠道执行此操作,并进行一些计数。

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